欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java中 避免过多的使用同步 博客分类: java疑惑 java面向对象编程企业应用面临的问题J2EE开发技术指南 javawaitnotifynotifyAllThread 

程序员文章站 2024-03-20 23:45:46
...

 避免过多的使用同步,过多的使用同步可能会导致性能降低,死锁,甚至不确定的行为。
 
 为了避免死锁的危险,在一个被同步的方法或者代码块中,永远不要放弃对客户的控制。
 
 
  在一个被同步的区域内部,不要调用一个不被改写的公有或者受保护的方法,从包含
  该同步区域的类的角度来看,这样的方法是一个外来者。
 
  可以可以为止这个外来方法提供一个实现,并且在该方法中创建另一个线程,在回调到这个类中。
  然后,新建的线程试图获取原线程所拥有的那把锁,这样会导致新建的线程被阻塞,如果创建该线程的方法正在等待这个线程完成任务,则死锁就形成了。
 
 
 不同区域之外被调用的外来方法被称为开放调用,除了可以避免死锁之外,开放调用可以极大地增加并发性,外来方法的运行时间可能会任意长,如
 果在同步区域内调用外来方法的话,那么在外来方法执行期间,其他线程要想访问共享对象都被不必要的拒绝。
 
 
 通常在同步区域内你应该尽可能少的工作。获得锁,检查共享数据,根据需要转换数据,然后放掉锁,如果你必须要执行某一个很少时的动作,则应该
 设法把这个方法移动到同步区域的外部。
 
 
 如果一个类或者静态方法以来于一个可变的静态域,那么他必须要在内部进行同步,即使它往往只用于单个线程,与共享实例不同。这种情况下,对于
 客户要执行外部同步是不可能的,因为不可能保证其他的客户也会执行外部同步。
 
 简而言之,为了避免死锁和数据破坏,千万不要从同步区域内部调用外来方法。
 
 
 永远不要在循环的外面调用wait
      Object.wait方法的作用是使用一个线程等待某个条件,他一定是在
 一个同步区域中被调用的,而且该同步区域锁住了被调用的对象。
 wait的方法的标准模式:
 <pre>
 synchronized (obj) {
  while(<condition does not hold >){
     obj.wait();
  }
  
  ...//perform action appropriate to condition
 }
 </pre>
 总是使用wait循环模式来调用wait方法。永远不要在循环的外面调用wait,循环被用于等待的前后测试条件。
 
 
    在等待之前测试条件,如果条件已经成立的话则跳过等待,这对于确保灵活性。
 如果条件已经成立,并且在线程等待之前notify(或者notifyAll)方法已经被调用过,
 则无法保证该线程将总会从等待中醒过来。
 

 

package com.etrip.effective;

import java.util.LinkedList;
import java.util.List;
/**
 * @author longgangbai
 * @date 2012-11-22
 * 
 */
public  abstract class WorkQueue {

	private final List queue=new LinkedList();
	private boolean stopped=false;
	
	protected WorkQueue(){
		new WorkerThread().start();
	}
	
	
	public final void enqueue(Object workItem){
		synchronized (queue) {
			queue.add(workItem);
			queue.notify();
		}
	}
	
	public final void stop(){
		synchronized (queue) {
			stopped=true;
			queue.notify();
		}
	}
	public abstract void processItem(Object workItem)throws InterruptedException;
	 
	
	private class WorkerThread extends Thread{
		   @Override
		   public void run(){
			   while(true){
				   Object workItem=null;
					synchronized (queue) {
							while(queue.isEmpty() && !stopped){
								try {
									queue.wait();
								} catch (InterruptedException e) {
									return;
								}
							}
							if(stopped){
								return ;
							}
							workItem=queue.remove(0);
							try {
								processItem(workItem);
							} catch (InterruptedException e) {
								return;
							}
					}
			   }
		   }
	   }
  
}

 

创建一个显示工作队列DisplayWorkQueue :

package com.etrip.effective;
/**
 * 
 * @author longgangbai
 */
public class DisplayWorkQueue extends WorkQueue {

	public  void processItem(Object workItem)throws InterruptedException
	{
	  System.out.println(workItem);	
	  Thread.sleep(1000);
	}
	
}

 

 

创建一个死锁的工作队列类:

package com.etrip.effective;
/**
 * 
 * @author longgangbai
 */
public class DeadLockQueue extends WorkQueue {

	public  void processItem(final Object workItem)throws InterruptedException
	{
	   Thread child=new Thread(){
		   public void run(){
			   enqueue(workItem);
		   }
	   };
	   child.start();
	   child.join();//dead lock
	}
	
}

 

 两个线程均需要获取对象锁,造成死锁的解决方法是修改workqueue中的同步方法:

修改之后如下:

  abstract class WorkQueueExt{
	  
		private final List queue=new LinkedList();
		private boolean stopped=false;
		
		protected WorkQueueExt(){
			new WorkerThread().start();
		}
		
		
		public final void enqueue(Object workItem){
			synchronized (queue) {
				queue.add(workItem);
				queue.notify();
			}
		}
		
		public final void stop(){
			synchronized (queue) {
				stopped=true;
				queue.notify();
			}
		}
		public abstract void processItem(Object workItem)throws InterruptedException;
		 
		
		private class WorkerThread extends Thread{
			   @Override
			   public void run(){
				   while(true){
					   Object workItem=null;
						synchronized (queue) {
								while(queue.isEmpty() && !stopped){
									try {
										queue.wait();
									} catch (InterruptedException e) {
										return;
									}
								}
								if(stopped){
									return ;
								}
								workItem=queue.remove(0);
						}
	                    //将代码放在同步之外,解除死锁					
						try {
							processItem(workItem);
						} catch (InterruptedException e) {
							return;
						}
				   }
			   }
		   }
	  
	}

 
 
      如果处于等待状态的所有线程都在等待同一个条件,而每次只有
 一个线程可以从这个条件中被唤醒,那么你可以选择调用notify而不是notifyAll.
     使用notifyAll优先于notify的建议有一个警告:虽然使用notifyAll不会影
响正确性,但是会影响性能。实际上,从系统角度来看,它会使某些数据结构的性能,从等待线程数的线性退化到平方级别。