1)竞争条件
在实际的多线程应用中,通常会有两个或多个线程需要对共同的对象进行共享访问,如果两个线程访问相同的对象,而且每一个都调用了一个会改变对象状态的方法,
那么,线程就会相互倾轧。根据各个线程访问数据的不同顺序,可能会产生腐蚀现象。这种情况通常称为竞争条件。
2)同步
为了多个线程对共享数据的腐蚀,就需要对数据的存取实现同步;常用的同步方法有3种:
1、Reenlock
用Reenlock保护代码块的基本机构如下:
1 Lock myLock=new ReenLock;
2
3 myLock.lock();
4 try
5 {
6
7 .....
8
9 }
10
11 finally
12 {
13
14 myLock.unlock();
15
16 }
这种结构保证在任何一个时刻只有一个线程能够进入临界区,一旦一个线程锁住了锁对象,其它任何线程都无法通过lock语句,当它们调用lock时,它们就会被阻塞,直到第一个线程释放锁对象。
如果一个线程进入到临界区,却发现它必须等待某个条件满足后才能执行;这时,就需要一个条件对象来管理那些已获得了锁,却不能开始执行任务的线程,条件变量一般使用方法如下:
1 Lock myLock=new ReenLock; 2 3 Condition myCondition = myLock.newCondition(); 4 5 myLock.lock(); 6 7 try 8 { 9 10 while(!(ok to proceed)) 11 12 myCondition.await(); 13 14 ..... 15 16 myCondition.signalAll(); 17 18 } 19 20 finally 21 { 22 23 myLock.unlock(); 24 25 }
当一个线程调用了await()方法之后,进入阻塞状态,直到有其它线程调用signalAll()方法;从被阻塞的地方继续执行。
2、Synchronized关键字
通过Synchronize关键字可以声明一些同步方法或是同步代码块,使用结构如下:
1 public Synchronized void method() 2 3 { 4 method body 5 .... 6 7 }
等价于:
1 public void method() 2 { 3 4 implicitLock.lock(); 5 6 try{ 7 8 method body 9 ... 10 11 } 12 13 finnally 14 { 15 16 implicitLock.unlock(); 17 18 } 19 20 }
隐式对象锁只有一个关联条件;wait方法将线程加入到等待集中,notifyAll方法解除线程的阻塞状态。
3、同步器
java.util.concurrent包包含了一些帮助人们来管理相互合作的线程:
类 | 它能做什么 | 何时使用它 |
CyclicBarrier | 允许一个线程集等待直至其中预定数量的线程达到一个公共障栅为止 ,然后可以选择执行一个处理障栅的动作 | 当大量的线程需要在它们的结果可用之前完成时 |
CountDownLatch | 允许一个线程等待直至计数器减为0为止 | 当一个或多个线程需要等待直至指定数量的结果可用为止 |
Exchanger | 允许两个线程在要交换的对象准备好时交换对象 | 当两个线程工作在同一个数据结构的两个实例上时,一个向实例添加数据,另一个从实例中清除数据 |
SynchronizedQueue | 允许一个线程将对象交给另一个线程 | 在没有显式同步的情况下,的那个两个线程准备好将一个对象从一个线程传给另一个时 |
Semaphore | 允许线程集等待直至被允许继续运行为止 | 用来限制访问资源的线程总数 |
BlockingQueue | 试图向一个满地阻塞队列添加元素或从一个空的阻塞队列移除元素时,将导致线程阻塞 |
平衡两个速度不同的线程的合作 |
4、同步方法的选取
a)优先选取java.util.concurrent包中的一种机制,它会为你处理所有的加锁;
b)如果synchronized关键字可以在你的程序中工作,尽量使用它;
c)只有在非常需要lock/condition结构的独有特性时,才会使用它。
5、Volatile 域
只为实例的一两个域使用同步,开销太大了;通常使用Volatile关键字为 实例的域提供一个免锁机制。
2014-07-23 20:56:46