当前位置:文档之家› Java并发编程实践

Java并发编程实践

9chunk
chunk1
1.1.3.4
让所有这个类下面的对象都同步的时候,也就是让所有这个类下面的对象共用同一把锁的时候,我们可以在区块中锁定类常量的方式进行。例如:
通过锁定lock对象,我们可以实现对这个类所有对象的同步。切记synchronized锁定的是对象,对象不同就不会同步。例如:
两个方法就不会同步一个锁定的是lock一个锁定的是类对象。
1.1.2
Java内存模型要求,非volatile类型的64位数值变量(double和long),JVM允许将64位的读操作和写操作分解为两个32位的操作。
那么在多线程的环境中,如果要读取非volatile类型的double、隆就有可能会读取到某个值的高32位和另一个值的低32位组成的一个数值。
但目前各种平台的商用虚拟机几乎都把64位数据的读写操作作为原子操作来对待。
volatile的第二种特性是禁止指令重排序。
Java内存模型对volatile变量的特殊规则如下:
在线程工作内存中,每次使用volatile变量,都必须先从主内存刷新最新的值,用于保证能及时看见其他线程对volatile变量所做的修改
每次修改volatile变量后都应立即同步到主内存中,用于保证其他线程可以看到对volatile变量所做的修改
3.除了在方法上用synchronized关键字外,也可以在方法内部的某个区块中用synchronized表示只对这个区块中的资源进行同步访问,例如synchronized(this){/**区块**/}的作用域就是当前对象。
1.1.3.1
运行结果是:chunk对象与chunk1对象锁互不干扰。
chunk1
9chunk
1.1.3.2
结果与上面一样,也是chunk与chunk1互不干扰
chunk1
9chunk
1.1.3.3
注意这段代码中的chunk与chunk1是互不干扰的,因为他们一个是static一个是非static方法。执行结果是:
chunk1
9chunk
这段代码中的chunk与chunk1就可以进行同步操作了。因为他们的锁是同一个锁,执行结果是:
在Java5.0中,Integer.toString()由之前的ThreadLocal对象保存12字节大小的缓冲区变成了每次调用时分配一个新的缓冲区,对于像临时缓冲区这样简单的对象,除非频繁操作否则ThreadLocal没有性能优势。
概念上,可以讲ThreadLocal<T>看成是map(Thread,T)对象,其中保存了特定于该线程的值,但事实并非如此,特定于线程的值保存在Thread对象中。
volatile修饰的变量需保证代码的执行顺序与程序的顺序相同
1.1.5
volatile的字面意思就是“易变的”,其意思就是告诉Java的内存模型存储在寄存器中存放的当前变量是不确定的,需要从主存中读取,修改完成后也要立即将变量写入到主存中去。而synchronized则是锁定的当前变量,只有当前线程才可以访问该变量,其他线程将被阻塞。
volatile只能由于变量级别,而synchronized却可以用于变量和方法级别
volatile仅能保证变量的修改可见性不能保证变量的修改原子性,而synchronized既能保证变量的修改可见性也能保证变量的修改原子性
volatile不会造成线程堵塞,而synchronized可能会造成线程阻塞
}
}
}
用工厂方法可以防止this引用逸出
1.3
线程封闭:只在单线程内访问数据,这种技术称为线程封闭
当对象被封闭在一个线程中,自动成为线程安全的,即使被封闭的对象本身不是线程安全的。如JDBC连接池,Connection对象并不是线程安全的,但是大多数请求(Servlet请求或者EJB调用)都是由单个线Leabharlann 采用同步的方式来处理,因封闭而安全。
ReentrantLock拥有synchronized相同的并发性和语义,此外还多了锁投票、定时锁等待和中断锁等待。例如线程A和线程B都要获得对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,如果使用synchronized,如果A不释放,B将一直等待下去无法中断。如果使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时候以后中断等待,而去干别的事情。
}
private class EscapeRunnable implements Runnable{
public void run(){
//通过ThisEscape.this就可以引用外围类对象,但是此时外围类对象还没有构造完成,即发生了外围类的this引用的逸出。所以在构造函数中创建Thread对象后不要启动Thread。可以提供一个start或者init方法负责启动线程。
每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且ThreadLocal实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
1.在某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象其他的synchronized方法(这个对象还有其他的synchronized方法,如果其中一个线程访问了其中一个synchronized方法,那么其他线程将不能访问此对象另外的synchronized方法)。但不同的对象实例间的synchronized方法是相互独立的,也就是说其他线程照样可以访问相同类的另一个对象实例的synchronized方法。
volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化
1.1.6
在代码层面上来说,synchronized类似面向对象,它可以修饰变量、方法、类、对象等。而Lock却像是面向过程,在需要的时候获取锁,结束的时候释放锁(一般是在finally中释放)。
从性能上来说,在低并发的情况下synchronized要比Lock性能好,在高并发的情况下,Lock要比synchronized的性能好很多。
1.1.3
加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。
同步代码块的锁就是方法调用所在的对象,静态synchronized方法以Class对象作为锁。
synchronized可以用于实例变量、对象引用、static方法、类名称字面常量。
原则:为防止逸出,对象必须要被完全构造完后,才可以被发布(最好的解决办法是采用同步)
this逸出是指构造函数返回之前其他线程就持有该对象的引用,this逸出经常发生在构造函数中启动线程和注册监听器。如
public class ThisEscape{
public ThisEscape(){
new Thread(new EscapeRunnable()).start();
synchronized是在JVM层面实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM将自动释放锁。Lock则不行,因为Lock通过代码实现它必须在finally中释放锁。synchronized在生成线程转储时生成文件对开发人员找出死锁原因很有帮助,而Lock对于查找死锁原因将很困难,这个问题在Java6中已经得到解决,它提供了一个管理和调试的接口,锁可以通过这个接口进行注册,并通过其他管理和调试接口,从线程转储中得到ReentrantLock的加锁信息。
1.4
当满足以下条件时,对象才是不可变的:
对象创建后其状态就不能再改变
对象的所有域都是final类型
对象是正确创建的(在对象的创建期间,this引用没有逸出)
特别说明:不可变对象的域并未全部声明为final类型。如String会惰性地(lazily)的计算哈希值:当第一次调用hashcode()时,String计算哈希值,并将它缓存在一个非final域中。这个域有一个非默认的值,在每次计算中得得到相同的结果。
例如:如果在线程内部发布了局部变量(一个collection对象)的引用或者将该collection对象中的数据发布,那么封闭性将会被破坏,导致collection对象的逸出
1.3.3
ThreadLocal对象通常用于防止对可变的单实例变量(Singleton)或全局变量进行共享。
当某个频繁执行的操作需要一个临时对象,例如一个缓冲区,而同时又希望避免在每次执行时都重新分配该临时对象。就可以使用ThreadLocal。但是除非这个操作的执行频率非常高,或者分配操作的开销非常高,否则ThreadLocal不能带来性能的提升。
volatile的使用情况如下:
运算结果不依赖于变量的当前值,或者能够确保只有单一线程修改变量的值
变量不需要与其他的状态变量共同参与不变形约束
例如:当需要检查某个状态标记以判断是否退出循环的时候可以考虑采用volatile,加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。
Synchronized获取锁和释放锁这样的行为是成对出现的,而ReentrantLock却不是这样的,举例来说在一个链表结构中,可以将ReentrantLock加到链表结构的每个节点上来减少锁的粒度,从而允许不同的线程操作链表的不同部分。
1.2
发布对象:指的是使它能够被当前范围之外的代码所使用,例如将一个指向该对象的引用保存到其他代码可以访问的地方,或者在某一个非私有的方法中返回该引用,或者将引用传递到其他类的方法中。
相关主题