java中 避免过多的使用同步 博客分类: java疑惑 java面向对象编程企业应用面临的问题J2EE开发技术指南 javawaitnotifynotifyAllThread
避免过多的使用同步,过多的使用同步可能会导致性能降低,死锁,甚至不确定的行为。
为了避免死锁的危险,在一个被同步的方法或者代码块中,永远不要放弃对客户的控制。
在一个被同步的区域内部,不要调用一个不被改写的公有或者受保护的方法,从包含
该同步区域的类的角度来看,这样的方法是一个外来者。
可以可以为止这个外来方法提供一个实现,并且在该方法中创建另一个线程,在回调到这个类中。
然后,新建的线程试图获取原线程所拥有的那把锁,这样会导致新建的线程被阻塞,如果创建该线程的方法正在等待这个线程完成任务,则死锁就形成了。
不同区域之外被调用的外来方法被称为开放调用,除了可以避免死锁之外,开放调用可以极大地增加并发性,外来方法的运行时间可能会任意长,如
果在同步区域内调用外来方法的话,那么在外来方法执行期间,其他线程要想访问共享对象都被不必要的拒绝。
通常在同步区域内你应该尽可能少的工作。获得锁,检查共享数据,根据需要转换数据,然后放掉锁,如果你必须要执行某一个很少时的动作,则应该
设法把这个方法移动到同步区域的外部。
如果一个类或者静态方法以来于一个可变的静态域,那么他必须要在内部进行同步,即使它往往只用于单个线程,与共享实例不同。这种情况下,对于
客户要执行外部同步是不可能的,因为不可能保证其他的客户也会执行外部同步。
简而言之,为了避免死锁和数据破坏,千万不要从同步区域内部调用外来方法。
永远不要在循环的外面调用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)方法已经被调用过,
则无法保证该线程将总会从等待中醒过来。
创建一个显示工作队列DisplayWorkQueue : 创建一个死锁的工作队列类: 两个线程均需要获取对象锁,造成死锁的解决方法是修改workqueue中的同步方法: 修改之后如下: 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;
}
}
}
}
}
}
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
}
}
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不会影
响正确性,但是会影响性能。实际上,从系统角度来看,它会使某些数据结构的性能,从等待线程数的线性退化到平方级别。