Java并发编程(详解wait(), notify(),sleep())

        上一篇博客,重点讲解了java中锁的机制,省的在多线程之间出现混乱的局面,其实主要能够理解钥匙即可。如果要保证方法之间能够独立完全的执行,因此就必须所有的方法都共用一把钥匙。然后小编最后也总结了一下,在此也再说一下。

        1.调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。

        2.调用同一个类中的静态同步方法的线程将彼此阻塞,他们都所锁在同一个class对象上

       上一篇博客中都提到过这些知识,不理解的可以参照上一篇博客。java并发编程(synchronized详解)

       这篇博客来讲一下,java中线程之间的同步技术,用到的也就是Object对象的几个方法,正如标题所示wait、notify,sleep方法,为了学习线程之间的同步技术,小编查阅了很多博客,在此也分享一下自己的学习经验。


                  这里详细的说一下各个方法的说明


        1.notify()

        具体是怎么个意思呢?就是用来唤醒在此对象上等待的单个线程。说的有点太专业。打个比方,现在有十栋大房子,里面有很多被上了锁的房间,奇怪的是锁都是一样的,更不可思议的是,现在只有一把钥匙。而此时,张三用完钥匙后,就会发出归还钥匙的提醒,就相当于发出notify()通知,但是要注意的是,此时钥匙还在张三手中,只不过,当张三发出notify()通知后,JVM从那些整个沉睡的线程,唤醒一个。对应本例子,就是从其余的九栋大房子中唤醒一家,至于提醒谁来拿这把钥匙,就看JVM如何分配资源了。等到张三把钥匙归还后,那个被提醒的哪家,就可以使用该把钥匙来开房间门了。与此相对应的,还有一个notifyAll()方法。这是什么意思呢,还是本例,张三嗓门大,这时吼了一嗓子,即notifyAll(),所有沉睡的线程全都被吵醒了,当张三归还钥匙后,他们就可以竞争了,注意,刚才是JVM自动分配,而此时是线程之间竞争,比如优先级等等条件,是有区别的。


 需要注意的是notify()方法执行后,并不是立即释放锁,而是等到加锁的代码块执行完后,才开始释放的,相当于本例中,张三只是发出了归还的通知,但是钥匙还没有归还,需要等到代码块,执行完后,才可以归还。


        2.wait()

        这个方法又是怎么个意思呢?当执行到这个方法时,就把钥匙归还,开始睡觉了。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。


        下面来看一个例子,加入我们要实现三个线程之间的同步操作,如何来实现呢?加入有三个线程分别用来输出A/B/C,如何能够三个线程之间顺序执行呢?这时候就需要采取线程之间同步的操作了,详见下面的代码。

<span style="font-family:Comic Sans MS;font-size:18px;">package com.test;


public class MyThreadPrinter2 implements Runnable {

	private String name;
	private Object prev;
	private Object self;

	private MyThreadPrinter2(String name, Object prev, Object self) {
		this.name = name; // A B C
		this.prev = prev; // c a b
		this.self = self; // a b c
	}

	@Override
	public void run() {
		int count = 10;
		while (count > 0) {
			// 加锁,锁的钥匙是prev
			synchronized (prev) {
				// 一把锁,锁的钥匙是self变量
				synchronized (self) {
					System.out.print(name);
					count--;
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					// 唤醒另一个线程,但是也需要把本线程执行完后,才可以释放锁
					self.notify(); //a b c
				}

				try {
					// 释放对象锁,本线程进入休眠状态,等待被唤醒
					prev.wait();  //睡觉觉了,等待被叫醒吧   // c a b
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		}
	}

	public static void main(String[] args) throws Exception {
		Object a = new Object();
		Object b = new Object();
		Object c = new Object();
		MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
		MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
		MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);

		new Thread(pa).start();
		// 这样才可以保证按照顺序执行
		 Thread.sleep(10);
		new Thread(pb).start();
		 Thread.sleep(10);
		new Thread(pc).start();
		 Thread.sleep(10);
	}
}</span>

        下面来分析一下,首先通过线程之间的同步操作,上面的例子就会按照线程的顺序来分别执行,最终输出的结果就是ABCABCABC..............。

        在此还是打一个比方,程序刚开始的时候,创建了三个线程的对象,也就是代表有三座大房子,下面开始执行了,第一个大房子里面有一个叫做张三的人员,打开了一个房子的钥匙c,然后拿着钥匙a,又一次打开了这个房子里面的一个箱子,最后完成后,把箱子的钥匙a给归还了,执行完后,张三开始在这个c钥匙的房子里面漫长的沉睡,也就是prev.wati()方法;当第二个人李四来到房子后,同样执行一边操作,此时需要注意的是,李四扔出的是箱子钥匙b,并在a钥匙的房间随着了;好吧懒货,王五来到了第三个大房子,同样执行了一遍操作,注意的是,王五扔出的是箱子钥匙c,在钥匙b房子睡着了。


        重点来了,王五扔出箱子钥匙c后,此时就把在房子钥匙c中的张三给唤醒了,等到王五把钥匙归还后,此时张三又开始周而复始的运作了;于是大家可以按照逻辑接着向下分析一下。


        在整个例子中需要注意一下几点

        1.为了保证几个线程先能够顺序执行,于是加入了Thread.sleep(10)

        2.每个对象执行wait()或者notify()方法时,只能在同一把锁的房子里面,例如

synchronized (self) {

System.out.print(name);

count--;

try {

Thread.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

// 唤醒另一个线程,但是也需要把本线程执行完后,才可以释放锁

self.notify(); //a b c

}


        房子的锁是self,所以执行self.notify()代表把我房子的钥匙给归还了。


        有了上面的分析过程,下面我们出一道题,传统线程同步通信技术,子线程循环10次,接着主线程循环100次,又回到子线程循环10次,接着再回到主线程又循环100次,如此循环50次。这个例子又如何来实现呢?小编只在这里贴出源码,网上有很多解释的,可以按照小编的逻辑来分析一下。

<span style="font-family:Comic Sans MS;font-size:18px;">package com.test;


public class TraditionalThreadCommunication {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		final Business business = new Business();
		new Thread(
				new Runnable() {
					
					@Override
					public void run() {
					
						for(int i=1;i<=50;i++){
							business.sub(i);
						}
						
					}
				}
		).start();
		
		for(int i=1;i<=50;i++){
			business.main(i);
		}
		
	}

}
  class Business {
	  private boolean bShouldSub = true;
	  public synchronized void sub(int i){
		  while(!bShouldSub){
			  try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		  }
			for(int j=1;j<=10;j++){
				System.out.println("sub thread sequence of " + j + ",loop of " + i);
			}
		  bShouldSub = false;
		  this.notify();
	  }
	  
	  public synchronized void main(int i){
		  	while(bShouldSub){
		  		try {
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		  	}
			for(int j=1;j<=100;j++){
				System.out.println("main thread sequence of " + j + ",loop of " + i);
			}
			bShouldSub = true;
			this.notify();
	  }
  }
</span>


  

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值