2014-02-04|3272阅|作者:java|举报 摘要:一、同步模型 最常见的例子就是银行取款,A,B两个人分别在银行对一个账户进行取款操作,账户仅有1000余额,当同事点击取款1000的按钮时,银行会怎样处理呢? 每个人都查询到了余额1000元没错,即使同时取款,也会有一方不成功的,否则的话,银行会被搞垮的,他才没那么笨,但是这
一、同步模型 最常见的例子就是银行取款,A,B两个人分别在银行对一个账户进行取款操作,账户仅有1000余额,当同事点击取款1000的按钮时,银行会怎样处理呢? 每个人都查询到了余额1000元没错,即使同时取款,也会有一方不成功的,否则的话,银行会被搞垮的,他才没那么笨,但是这该如何实现呢? 1、synchronized同步关键字 这里涉及到多个线程对共享资源进行处理,这里就必须要给线程“排队”,使用的关键字是synchronized(同步),在程序中,给对共享资源进行处理的代码部分(整个方法或代码块)添加此关键字。相当于给代码块加锁,取得钥匙的线程对资源操作,完成后将钥匙交予其他线程,这保证了同一个资源某一时间内只有一个线程可以对其修改等操作。 public void func(){ //代码 synchronized(锁){ //对共享资源处理代码 } //代码 } 这里注意: (1)、锁本身必须是共享的资源,不能使局部变量,如在方法内部定义锁,然后使用的话,这样的代码是无意义的,每个对象都会实例化一个锁,锁就失去了作用。比如超市的储物柜钥匙如果每人一把的话,那谁还敢把东西放进去呢。 public void func(){ Object lock = new Object(); //代码 synchronized(lock){//锁失去了意义 //对共享资源处理代码 } //代码 } (2)、锁可以是任何一个类型即Object (3)、注意细分锁。可以有多个锁对象,对不同的资源“上锁” class A{ private Object lock1 = new Object(); private Object lock2 = new Object(); void f(){ synchronized(lock1){ //访问资源P } } void g(){ synchronized(lock2){ //访问资源Q } } } 2、Lock 锁对象 除了synchronized还可以用Lock创建临界区 class A{ Lock lock = new ReentrantLock(); void f(){ lock.lock();//加锁 try{ //访问资源 }finally{ lock.unlock();//解锁 } } ] 3、synchronized 和 Lock (1)Lock的锁定时通过代码实现的,synchronized是在JVM层面上实现的 (2)synchronized在锁定时如果出现方法块抛出异常,JVM会自动解锁,不会造成因为异常而死锁。 Lock在出现异常时只能在finally{}将锁释放。 (3)Lock能完成synchronized所实现的所有功能 (4)Lock有比synchronized更精确的线程语义和更好的性能。 (5)在资源竞争不是很激烈的情况下,synchronized的性能优于ReentrantLock,但是当资源竞争很激烈时,synchronized的性能会下降几十倍,而ReentrantLock的性能维持常态。 二、生产/消费模型 生产者生产一件产品放到仓库,消费者从仓库取走产品,生产者再生产一件... 有几种方式呢? 1、消费者不停的问“有货吗?有货吗?有货吗?”。这无疑是很浪费资源的方式 2、wait()/notify() 消费者处于wait(),生产者生产出产品则notify(),然后wait()消费者收到信息进行消费,然后notify生产者,并在此处于wait(),生产者再继续生产... 生产者类的代码;
-
/**
-
* 生产者线程:无数据时再存,存入一个就发通知
-
* @author Administrator
-
*
-
*/
-
public class ProduceThread extends Thread{
-
//用来标记放入对象的每一个独立ID号
-
private static int count = 0;
-
//与消费线程或共同存取的对象列表
-
private List shareList;
-
-
public ProduceThread(List shareList){
-
this.shareList = shareList;
-
}
-
-
public void run(){
-
System.out.println("生产线程已经启动 。。。"+shareList.size());
-
while(true){
-
try{
-
Thread.sleep(2000);
-
synchronized(shareList){
-
while(shareList.size()>0){
-
shareList.wait();//有货,等待
-
}
-
while(shareList.size()==0){//无货,生产
-
Student st = new Student();
-
count++;
-
st.id = count;
-
st.name = count+"aaa";
-
System.out.println("--->生产线程放入对象:"+st.toString());
-
shareList.add(st);
-
shareList.notify();//通知
-
}
-
}
-
}catch(Exception ef){
-
ef.printStackTrace();
-
}
-
}
-
}
-
}
消费者类的代码:
-
/**
-
* 取数据线程:当有数据时取,取完通知
-
* @author Administrator
-
*
-
*/
-
public class CustomerThread extends Thread{
-
-
private List shareList;
-
-
public CustomerThread(List shareList){
-
this.shareList = shareList;
-
}
-
-
public void run(){
-
System.out.println("消费线程已经启动。。。"+shareList.size());
-
while(true){
-
try{
-
synchronized(shareList){
-
while(shareList.size()==0){
-
//如果没有,消费线程则等待
-
shareList.wait();
-
}
-
while(shareList.size()>0){
-
System.out.println("<---消费线程取出 "+shareList.remove(0).toString());
-
//取了一个已经,立即发出通知
-
shareList.notify();
-
}
-
}
-
}catch(Exception ef){
-
ef.printStackTrace();
-
}
-
}
-
}
-
}
注意wait()和sleep()的区别是: wait()释放锁等待,而sleep()是不释放锁的,即,它在休眠的时候仍然占用资源。 三、双缓冲模型 双缓冲模型有两个队列A,B,生产者将产品放到队列A中,消费者从队列B中拿产品,当B队列为空时,将A队列的产品存入B。当B不为空时,消费者只管取货,生产者只管生产,两者之间没有直接的联系,只有B为空时,将B队列同步起来,再将A同步起来,A中产品倒入B。 这样,就可以大大减少处理同步的次数。