java线程中断的本质,简单的设计思想(转)

    本文部分内容转自:http://blog.csdn.net/dlite/article/details/4218105,原文作者为:DLite。仅部分摘录。

    一直以来,用过很多线程的应用,最经常使用的就是开启一个线程,然后就不管之,任其运行。或者运行成功,或者运行失败,都跟主线程无关。稍微用得多一点的就是使用对象的wait,再配合线程之间的调度,当线程需要中断时,直接中断其,然后被中断的线程抛出一个中断异常,自然地被中止运行了(因为没有捕获异常,被中断线程方法自然停止运行)。

    但是长久地来看,并没有了解线程中断的实质。从简单上来讲,中断一个线程,可以理解为中断这个线程的运行,就像使用thread.stop方法所运行的一样;并且实际运行效果也是这样,因为线程抛出了异常,而我们没捕获,则线程自然被中断,听起来中断一个线程就和停止线程运行一个效果。但实际上,这是两个完全不一样的工作方式。
    所谓的线程停止,是指这个线程已经不再运行了;而中断,则是指线程由于某种原因被外力(或自身)要求停止不再运行,但线程仍然拥有是否继续运行(或停止运行)的权利,关键在于线程如何处理这个中断的动作。简单一点理解就是,线程中断,只是向线程发送了一个标记,表示这个线程应该中断了,但如何停止运行是由线程自身或处理中断的行为所决定的。
        以下为引用内容:

    JVM内部确实为每个线程维护了一个中断标记。
    通常情况下,调用线程的interrupt方法,并不能立即引发中断,只是设置了JVM内部的中断标记。因此,通过检查中断标记,应用程序可以做一些特殊操作,也可以完全忽略中断。
    你可能想,如果JVM只提供了这种简陋的中断机制,那和应用程序自己定义中断变量并轮询的方法相比,基本也没有什么优势。
    JVM内部中断变量的主要优势,就是对于某些情况,提供了模拟自动“中断陷入”的机制。
    在执行涉及线程调度的阻塞调用时(例如wait、sleep和join),如果发生中断,被阻塞线程会“尽可能快的”抛出InterruptedException。
    所谓“尽可能快”,我猜测JVM就是在线程调度调度的间隙检查中断变量,速度取决于JVM的实现和硬件的性能。

    另外,有jdk源代码为证,下方法为thread的interrupt方法:

    public void interrupt() {
	if (this != Thread.currentThread())
	    checkAccess();

	synchronized (blockerLock) {
	    Interruptible b = blocker;
	    if (b != null) {
//以下此方法,包括调用2,均只设置一个中断标记,下一行的英文注解为原jdk注解
		interrupt0();		// Just to set the interrupt flag
		b.interrupt();
		return;
	    }
	}
	interrupt0();//调用2
    }

    所以,线程中断就是为指定的线程设定一个中断变量,然后当被操作的线程接收到这个变量之后,就努力地抛出一个中断异常出来。由于在一般情况下,我们没有catch这个异常,所以,线程被自然地异常退出了,而且不会影响到主线程。
    在正式地线程调试中,肯定需要对线程的中断行为,定制不同的处理方式,比如安全地释放资源,然后安全的退出等。

继续阅读“java线程中断的本质,简单的设计思想(转)”

总结java内存数据的可见性和dcl的根本问题原因

    本文主要内容为网络收集,经过加工处理,参考文章见附录。
    说到java的并发编程,就必须要了解在并发中的一些数据的读取,特别是关键性数据的读取。为防止由于多线程的访问对于数据的读取产生读取顺序上的不一致,java引入了内存模型的概念,其根本概念即happen-before规则。

    happen-before规定在jvm实现内部,各个运行中的变量的读取顺序以及它对于多个线程在读取上的可见性。因为,在多个线程进行同时操作时,变量会根据每个线程保存它自己的一个副本(类似于clone),并且在必要的时候同主存(即原始的数据)同步一次。正是因为有这样的概念,在多线程中,我们看到的对象的数据值,可能就不是最新的一个数据值,而是一个过期的数据,或者说是一个错误的数据。而基于想象中正确的逻辑进行编程的话,就可能出现一些问题。
    happens-before就是“什么什么一定在什么什么之前运行”,也就是保证顺序性。即在多个线程或者同一个线程的不同操作之间,要满足说哪个操作一定会在哪个操作之前发生。那么在相对靠后的操作中使用的变量所读取的值,则一定是在操作之前写入的数据,不会读取到还未写入的值。

继续阅读“总结java内存数据的可见性和dcl的根本问题原因”

使用javaMail创建html邮件

    本文部分转自:haolloyin JavaMail入门:创建纯文本、HTML格式的邮件         JavaMail:邮件发送以及sina、163、QQ服务器不同的解析结果(附图)

    首先,可以通过以下maven下载相应的mail包

		<dependency>
			<groupId>javax.mail</groupId>
			<artifactId>mail</artifactId>
			<version>1.4.1</version>
		</dependency>

    本文,通过原始的java mail描述mail的发送过程,以及使用spring的mail模块如何进行邮件的发送。

继续阅读“使用javaMail创建html邮件”

(转)java编码问题详解

    本文转自:http://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/index.html?ca=drs- (原文作者 许令波)

    编码问题一直困扰着开发人员,尤其在 Java 中更加明显,因为 Java 是跨平台语言,不同平台之间编码之间的切换较多。本文将向你详细介绍 Java 中编码问题出现的根本原因,你将了解到:Java 中经常遇到的几种编码格式的区别;Java 中经常需要编码的场景;出现中文问题的原因分析;在开发 Java web 程序时可能会存在编码的几个地方,一个 HTTP 请求怎么控制编码格式?如何避免出现中文问题?

    为什么要编码
    不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码?要回答这个问题必须要回到计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言。由于人类的语言有太多,因而表示这些语言的符号太多,无法用计算机中一个基本的存储单元—— byte 来表示,因而必须要经过拆分或一些翻译工作,才能让计算机能理解。我们可以把计算机能够理解的语言假定为英语,其它语言要能够在计算机中使用必须经过一次翻译,把它翻译成英语。这个翻译的过程就是编码。所以可以想象只要不是说英语的国家要能够使用计算机就必须要经过编码。这看起来有些霸道,但是这就是现状,这也和我们国家现在在大力推广汉语一样,希望其它国家都会说汉语,以后其它的语言都翻译成汉语,我们可以把计算机中存储信息的最小单位改成汉字,这样我们就不存在编码问题了。

    所以总的来说,编码的原因可以总结为:

  1. 计算机中存储信息的最小单元是一个字节即 8 个 bit,所以能表示的字符范围是 0~255 个
  2. 人类要表示的符号太多,无法用一个字节来完全表示
  3. 要解决这个矛盾必须需要一个新的数据结构 char,从 char 到 byte 必须编码

继续阅读“(转)java编码问题详解”

理解合并排序,java中使用的标准排序方法

    所谓合并排序,就是将两个部分的数组合并在一起,但是有个前提就是两个数组本身就是已经排好序的。它包括两个问题,一个合并,一是排序。
    基本思想就是将一个数组从中间分为两个部分,先对左边排序,再对右边排序,最后将两边再合并出来即可了。采用递归手段,我们可以递归的对左边排序,对右边排序,最后合并。

    由于要将两个子数组合并为一个新的数组,因此需要一个额外的空间来存储这些数据信息。因此,我们需要建立一个与原数组大小相同的数组,用于合并过程。合并完了之后,再将额外空间中的数据copy回原来的空间,即完成整个排序工作。

    现在来看程序逻辑,程序逻辑已按照先分面两个部分,再分别递归排序,然后再合并的思路,具体代码如下:

	/** 归并排序,ints为要排序的数组 */
	public static void guibingSort(int[] ints) {
		int[] temp = new int[ints.length];
		guibingSort(ints, temp, 0, ints.length - 1);
	}

	/** 归并排序,ints为要进行排序的数组,temp为临时数组,start为排序起点,end为排序终点,排序范围为[start,end] */
	private static void guibingSort(int[] ints, int[] temp, int start, int end) {
		if(start >= end)
			return;
		int middle = (start + end) / 2;//中间点
		int left = start;//左边起点
		int right = middle + 1;//右边起点
		guibingSort(ints, temp, left, middle);//左边排序
		guibingSort(ints, temp, right, end);//右边排序
		//将两边的数组进行合并
		int i;
		for(i = start;  left <=  middle && right <= end;) {
			if(ints[left] > ints[right])
				temp[i++] = ints[right++];
			else
				temp[i++] = ints[left++];
		}
		//将左边没有合并完的添加到temp中
		while(left <= middle)
			temp[i++] = ints[left++];
		//将右边没有合并完的添加到temp中
		while(right <= end)
			temp[i++] = ints[right++];
		//最后将这些数字转回到ints
		System.arraycopy(temp, start, ints, start, end - start + 1);
	}

    代码的主要部分在于合并,合并的思路即对两个数组从开头分别比较,将比较小的那个加入到额外数组中去,这个过程一直持续到其中有一个数组完成,最后将剩下数组的剩余部分全部copy至额外数组即可(因为,两个数组是排好序的,在copy过程中可以保证排序的正确性)。

    合并排序与快速排序的最大一个区别在于,合并排序需要使用额外的空间,而且数据在排序过程中需要进行不断的进行数据之间的复制,并且用于比较的次数较小;而快速排序则需要对数据进行不断的移动,而不需要进行数据复制,却需要进行大量的比较操作。因为在java中,所有的数据都是按引用进行排序,数据移动和复制都是走引用,因此,花费较小,并且进行对象间的比较花费较大,因此在进行对象比较时,都是使用的合并排序算法。而其它语言,如c++,则是使用快速排序来进行排序,他们依赖的即是对象的大小比较。

hibernate中的qbc(query by criteria)不支持having

    最近在做一次查询时,需要对查询的结果进行二次过滤,即分组之后再过滤,这种情况下,需要使用having。如以下的例子,我需要查询一个字段和另一个字段的和,并对记录进行分组,但结果集中并不需要总记录数为0的结果。sql语句如下所示:

select a, sum(b) as bsum from t where clause1=? group by a having and sum(b) > 0

    这种查询语句,使用hql能够很好地写出来,并且能够很好的运行。但如果条件数不定时,使用hql就需要动态的拼接hql,并且需要根据参数来设置参数。如下所示:

if(StringUtils.hasText("clause1"))
			hql += "clause1 = :clause1";
		//.....其它判断和处理
		if(StringUtils.hasText("clause1"))
			query.setString("clause1", clause1);

    在这种情况下,许多的if判断,无论是在编码还是在逻辑处理上都让人麻烦。对于不定的参数查询,qbc是最合适的(当然还有qbe)。但是对于qbc,处理having的这种情况即是不能处理的。
    不管使用任何处理情况,都不能从criteria中构建出这个having子句。

    经过google之后,网上有一个hibernate的bug报告HHH-1043,http://opensource.atlassian.com/projects/hibernate/browse/HHH-1043 。这个链接中,论述了在hibernate中的问题,并且提供一些解决方案,但这些解决方案都不能很容易的理解和解决问题。

    最根本的问题在于,qbc中的criteria,在hibernate内部设计和编码时,都是作为where条件中的一部分来实现的.主要的代码在类CriteriaQueryTranslator的方法getWhereCondition中,在这个方法中,迭代所有的criteria,并作为where条件使用。作为用在group集合中的having子句,并不属于where条件中任何一句。如果强行在criteria中使用having(比如使用Restrictions.sqlRestriction),最终的结果就是将having最终加到了where中条件的一部分。不过,这样生成的sql,即是错的,不会得到任何结果。

    直到最新的hibernate 3.6版本,此问题仍然未解决。对于需要处理聚集函数的条件问题,现阶段,除了hql(当然sql也算)。hibernate还没有其它的方法。先就这样吧。