07____多线程实现线程通信(Lock和Condition)

Local锁:


Local锁也是在java并发库java.util.concurrent中,不过Lock是一个接口,需要使用时必须new出Lock的具体实现类。
那么,Local锁是怎样使用呢,还是以账户存取款为例子,通过和synchronized锁的对比一看就会明白:


public class Test {
    public static int cash = 100;

    public static void main(String[] args) {
        
        Thread thread2 = new Thread(new Runnable() {

            public  void run() {
                                //synchronized(this)//此时锁不起作用
                 synchronized(Test.class){
                     System.out.println("run查看账户,余额为" + cash);
                     cash += 1000;
                    try {
                         Thread.sleep(5000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                    }
                    System.out.println("cash2 = " + cash);
                }
            }
        }

        );
        thread2.start();
        Thread thread1 = new Thread(new Runnable() {

            public  void run() {

                                //synchronized(this)此时锁不起作用,必须用Test1.class才起作用
                synchronized(Test.class){
                    System.out.println("m查看账户,余额为" + cash);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    cash = cash - 100;
                    System.out.println("cash1 = " + cash);
                }
            }
            
        });
        thread1.start();
    }
}

在学习synchronized关键字的时我们讨论了这个小例子,当synchronized()中的参数为this时,是不起作用的。因为两个
Thread中this指的不是同一个东西。只有使用Test..class的时候,锁才起作用。
我们将synchronized关键字换为Lock锁:
因为Lock是一个接口,所以我们使用时就要new出Lock锁的具体实现:

Lock lock = new ReentrantLock();
lock.lock();//上锁
lock.unlock();//解锁

修改后:
public class Test {
    public static int cash = 100;

    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();
        
        Thread thread2 = new Thread(new Runnable() {

            public  void run() {
                try{
                    lock.lock();//加锁
                    {
                        System.out.println("run查看账户,余额为" + cash);
                        cash += 1000;
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("cash2 = " + cash);
                    }
                }catch (Exception e) {
                    e.printStackTrace();
                }finally{
                    //关闭锁
                   lock.unlock();
                }
                
            }
        }

        );
        thread2.start();
        Thread thread1 = new Thread(new Runnable() {

            public  void run() {
                try{
                    lock.lock();//加锁
                    {
                        System.out.println("m查看账户,余额为" + cash);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        cash = cash - 100;
                        System.out.println("cash1 = " + cash);
                    }
                }catch(Exception e){
                    e.printStackTrace();
                }
                lock.unlock();//关闭锁
            }
            
        });
        thread1.start();
    }
}
此时的输出结果:

run查看账户,余额为100
cash2 = 1100
m查看账户,余额为1100
cash1 = 1000

结论:

程序运行正常。通过这个例子我们就能看出,Lock锁通过new出来,加了这把锁的所有方法不论在哪,都实现了线程阻塞。
相当于synchronized(常量),是个万能锁。

一个线程要加不同的锁,那么多new几个Lock,相同的lock加在需要阻塞的方法上就行了。

和synchronized不同的是,在线程执行完以后,要关闭锁unlock(),如果不关闭,其他在等待的线程就永远被锁在外面了。


比如new出lock和lock1,lock加在了方法A()和B()上,lock1加在了方法C()和D()上,那么,一旦有线程进入方法A(),就不会有线程再进入方法B(),同理,一旦有线程进入方法C(),就不会有线程进入方法D();但是A()和C(),B()和D()之间并没有形成阻塞。

Lock锁更加面向对象,因为lock本身就是一个对象。


Condition

Condition,Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

例子1:

启动两个线程,子线程运行10次,主线程运行10次,然后又子线程运行10次,主线程运行10次,如此反复50次:

public class Test{

    public static void main(String[] args) {
        final Busniess test = new Test().new Busniess();

        new Thread(new Runnable() {

            public void run() {
                for (int i = 1; i < 51; i++) {
                    test.sub(i);
                }
            }
        }).start();

        for (int i = 1; i < 51; i++) {
            test.main(i);
        }

    }

    class Busniess {
        boolean isSub = true;
        Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();   

        public void sub(int i) {
            lock.lock();
            try {
                while (!isSub) {
                    //this.wait();线程等待,保证子线程先运行
                    condition.await();
                }
                for (int j = 1; j < 11; j++) {
                    System.out.println("sub运行第" + i + "轮,第" + j + "次");
                }
                isSub = false;
                //this.notifyAll();线程唤醒
                 condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        public synchronized void main(int i) {
                try {
                    lock.lock();
                    while (isSub) { 
                        //this.wait();
                        condition.await();
                        for (int j = 1; j < 11; j++) {
                            System.out.println("main运行第" + i + "轮,第" + j + "次");
                        }
                        isSub = true;
                        //this.notifyAll();
                        condition.signalAll();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }finally{
                    lock.unlock();
                }
            
        }
    }
}
打印结果:

sub运行第1轮,第1次
sub运行第1轮,第2次
sub运行第1轮,第3次
sub运行第1轮,第4次
sub运行第1轮,第5次
sub运行第1轮,第6次
sub运行第1轮,第7次
sub运行第1轮,第8次
sub运行第1轮,第9次
sub运行第1轮,第10次
main运行第1轮,第1次
main运行第1轮,第2次
main运行第1轮,第3次
main运行第1轮,第4次
main运行第1轮,第5次
main运行第1轮,第6次
main运行第1轮,第7次
main运行第1轮,第8次
main运行第1轮,第9次
main运行第1轮,第10次
......

在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现。

Condition实例实质上被绑定到一个锁上。要为特定Lock 实例获得Condition 实例,请使用其newCondition() 方法。


Condition和传统的线程通信没什么区别,Condition的强大之处在于它可以为多个线程间建立不同的Condition,具体看下面的例子:

} 假定有一个绑定的缓冲区,它支持 puttake 方法。如果试图在空的缓冲区上执行take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存put 线程和take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个Condition 实例来做到这一点。

    class BoundedBuffer {  
       final Lock lock = new ReentrantLock();//锁对象  
       final Condition notFull  = lock.newCondition();//写线程条件   
       final Condition notEmpty = lock.newCondition();//读线程条件   
      
       final Object[] items = new Object[100];//缓存队列  
       int putptr/*写索引*/, takeptr/*读索引*/, count/*队列中存在的数据个数*/;  
      
       public void put(Object x) throws InterruptedException {  
         lock.lock();  
         try {  
           while (count == items.length)//如果队列满了   
             notFull.await();//阻塞写线程  
             items[putptr] = x;//赋值   
             if (++putptr == items.length) putptr = 0;//如果写索引写到队列的最后一个位置了,那么置为0  
             ++count;//个数++  
             notEmpty.signal();//唤醒读线程  
         } finally {  
             lock.unlock();  
         }  
       }  
      
       public Object take() throws InterruptedException {  
         lock.lock();  
         try {  
           while (count == 0)//如果队列为空  
             notEmpty.await();//阻塞读线程  
             Object x = items[takeptr];//取值   
             if (++takeptr == items.length) takeptr = 0;//如果读索引读到队列的最后一个位置了,那么置为0  
             --count;//个数--  
             notFull.signal();//唤醒写线程  
             return x;  
          } finally {  
             lock.unlock();  
         }  
       }   
     }  

例子2:

以此类推,如果有三个线程,想让线程1运行完以后运行线程2,线程2运行完以后运行线程3,线程3运行完以后又运行线程1,那么该如何来实现呢?

public class ThreeConditionCommunication {

	public static void main(String[] args) {

		final Business business = new Business();
		new Thread(new Runnable() {

			public void run() {

				for (int i = 1; i <= 50; i++) {
					business.sub2(i);
				}

			}
		}).start();

		new Thread(new Runnable() {

			public void run() {

				for (int i = 1; i <= 50; i++) {
					business.sub3(i);
				}

			}
		}).start();

		for (int i = 1; i <= 50; i++) {
			business.main(i);
		}

	}

	static class Business {
		Lock lock = new ReentrantLock();
		Condition condition1 = lock.newCondition();
		Condition condition2 = lock.newCondition();
		Condition condition3 = lock.newCondition();
		private int shouldSub = 1;

		public void sub2(int i) {
			lock.lock();
			try {
				while (shouldSub != 2) {
					try {
						condition2.await();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 10; j++) {
					System.out.println("sub2 thread sequence of " + j
							+ ",loop of " + i);
				}
				shouldSub = 3;
				condition3.signal();
			} finally {
				lock.unlock();
			}
		}

		public void sub3(int i) {
			lock.lock();
			try {
				while (shouldSub != 3) {
					try {
						condition3.await();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 20; j++) {
					System.out.println("sub3 thread sequence of " + j
							+ ",loop of " + i);
				}
				shouldSub = 1;
				condition1.signal();
			} finally {
				lock.unlock();
			}
		}

		public void main(int i) {
			lock.lock();
			try {
				while (shouldSub != 1) {
					try {
						condition1.await();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 10; j++) {
					System.out.println("main thread sequence of " + j
							+ ",loop of " + i);
				}
				shouldSub = 2;
				condition2.signal();
			} finally {
				lock.unlock();
			}
		}
	}
}

也就是说:Condition可以控制多个线程之间的运行顺序。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值