java 协调执行_java笔记六:线程间的协调

昨天重新学习了java多线程的使用,多线程的难点就在线程之间的协调。在《操作系统》一课中,我们学习了进程,其实多线程和多进程一样,都会涉及到多个进程或者线程对某一资源共享访问的问题,当多个线程都需要修改这个资源的时候就会出现线程安全问题。

比如说在银行开个账户会有一个存折和一张卡,如果某一天同一时间丈夫拿着存折去柜台取钱,而妻子拿着银行卡去ATM取钱。当丈夫查询余额里面有3000元,正准备取2000元,这时候妻子也到ATM里面查询也有3000,也取2000元。其实银行不可能让我们这么做,如果这样的话那我们天天取钱去了,还搞什么工作啊。其实在丈夫查询的时候已经对该账号上了锁,另外的银行卡要取钱的话必须等待该锁被释放。下面用一个程序模拟这个例子:

1 packagecom.sync;2

3 public class TestSync2 implementsRunnable{4 public BankCard bc = newBankCard();5 public static voidmain(String[] args) {6 TestSync2 test = newTestSync2();7 Thread wife = newThread(test);8 Thread husband = newThread(test);9 wife.setName("wife");10 husband.setName("husband");11 wife.start();12 husband.start();13 }14 public voidrun() {15 bc.getMoney(Thread.currentThread().getName(), 2000);16 }17 }18 classBankCard{19 private static int money = 3000;//模拟账户余额

20 public synchronized void getMoney(String name,intm){21 //synchronized(this){

22 try{23 Thread.sleep(1);24 } catch(InterruptedException e) {25 e.printStackTrace();26 }27 if(money >m){28 System.out.println(name+"取走了"+m+"元");29 money = money -m;30 }else{31 System.out.println("对不起,您的余额不足!");32 }33 //}

34 }35 }

上面的例子如果在getMoney()方法上面不加synchronized关键字的话,输出结果为:

wife取走了2000元

husband取走了2000元

而加上synchronized后,输出结果为:

wife取走了2000元

对不起,您的余额不足!

上面两种情况说明,如果多个线程同时访问某个资源,而不给该资源枷锁的话,就会出现问题。而加上synchronized关键字后就可以避免这种错误发生了。它能够保证只有一个线程能够访问getMoney()这个方法,其他药访问该方法的线程必须等待。

锁住某个资源可以用synchronized关键字来修饰一个方法或者同步代码块,这样能保证同一时间只能由一个线程访问该资源。

①、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

②、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

③、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

我们都知道,操作系统中多个进程之间如果不进行协调就很容易出现死锁的情况,死锁的四个条件:互斥、占有等待、非剥夺、循环等待。我们只要破坏其中一个条件就能避免死锁发生。线程之间也容易出现死锁,下面这个例子就演示了死锁的情况:

1 packagecom.sync;2

3 importcom.thread.SleepTest;4

5

6 public class TestDeadLock implementsRunnable{7 int flag = 1;8 static Object o1 = newObject();9 static Object o2 = newObject();10 public voidrun() {11 System.out.println(flag);12 if(flag == 1){13 synchronized(o1) {14 try{15 Thread.sleep(1000);16 } catch(InterruptedException e) {17 e.printStackTrace();18 }19 synchronized(o2) {20 System.out.println("1");21 }22 }23 }24 if(flag == 0){25 synchronized (o2) {//锁住某个对象,相当于占有该对象不让其他人使用

26 try{27 Thread.sleep(1000);28 } catch(InterruptedException e) {29 e.printStackTrace();30 }31 synchronized(o1) {32 System.out.println("0");33 }34 }35 }36 }37 public static voidmain(String[] args) {38 TestDeadLock t1 = newTestDeadLock();39 TestDeadLock t2 = newTestDeadLock();40 t1.flag = 1;41 t2.flag = 0;42 newThread(t1).start();43 newThread(t2).start();44 }45 }

运行程序输出1    0后就进入死锁状态,该程序永远也不会停止,因为两个线程同时处于等待状态。线程t1锁住了o1对象,等待o2对象,而线程t2锁住o2等待o2对象,谁也不让谁,这就进入了一个循环占有等待的情况了,死锁也就出现了。

所以,如果多个线程如果不进行协调的话很容易出现死锁的问题。操作系统中使用进程调度来协调各个进程,那么java重如何对各个线程进行协调呢?

java中主要使用Object类中的wait()、notify()、notifyAll()方法来协调各个线程。典型的例子有哲学家吃饭问题、生产者和消费者问题、理发师问题。下面一个用一个例子来演示生产者和消费者问题。

问题描述:生产者负责做馒头,做好馒头后放进指定的篓子里面,消费者消费该篓子里面的馒头。篓子里只能装一定量的馒头,满了以后生产者必须进入等待状态,消费者吃完馒头后也必须进入等待状态。

1 packagecom.sync;2

3 public classProductAndConsumer {4 public static voidmain(String[] args) {5 Basket b = newBasket();6 Product p = newProduct(b);7 Consumer c = newConsumer(b);8 newThread(p).start();9 newThread(c).start();10 }11 }12

13 classManTou{14 intid;15 public ManTou(intid) {16 this.id =id;17 }18 @Override19 publicString toString() {20 return "ManTou"+id;21 }22 }23

24 //装馒头的篮子

25 classBasket{26 int index = 0; //相当于栈顶指针

27 ManTou[] manTous = new ManTou[6];28 //往篮子里面放馒头

29 public synchronized voidpush(ManTou m){30 while(index ==manTous.length){31 try{32 this.wait();33 } catch(InterruptedException e) {34 e.printStackTrace();35 }36 }37 this.notify();38 manTous[index] =m;39 index++;40 }41 //往篮子里面取馒头

42 public synchronizedManTou pop(){43 while(index == 0){44 try{45 this.wait();46 } catch(InterruptedException e) {47 e.printStackTrace();48 }49 }50 this.notify();51 index--;52 returnmanTous[index];53 }54 }55 //生产者

56 class Product implementsRunnable{57 Basket basket;58 publicProduct(Basket basket) {59 this.basket =basket;60 }61 public voidrun() {62 for (int i = 0; i < 20; i++) {63 ManTou m = newManTou(i);64 basket.push(m);65 System.out.println("生产了"+m);66 try{67 Thread.sleep(1);68 } catch(InterruptedException e) {69 e.printStackTrace();70 }71

72 }73 }74 }75

76 //消费者

77 class Consumer implementsRunnable{78 Basket basket;79 publicConsumer(Basket basket) {80 this.basket =basket;81 }82 public voidrun() {83 for (int i = 0; i < 20; i++) {84 ManTou m =basket.pop();85 System.out.println("消费了"+m);86 try{87 Thread.sleep((int)(Math.random()*1000));88 } catch(InterruptedException e) {89 e.printStackTrace();90 }91 }92 }93 }

wait()、notify()、notifyAll()方法的作用:

wait():导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。

notify():唤醒在此对象监视器上等待的单个线程。

notifyAll():唤醒在此对象监视器上等待的所有线程。

wait()与sleep()的区别:

两个方法的共同点就是让当前线程进入等待状态。

不同点:

wait()之后,锁就不归我所有了,必须等醒过来后才能拥有该锁,并且必须要有人唤醒它才会醒过来

sleep()不同,锁还是归我所有,一段时间后会自动醒过来

已标记关键词 清除标记
表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
<span style="color:#404040;">本教程特点:</span><br /><span style="color:#404040;">1.更适合零基础学员:</span><br /><span style="color:#404040;">·自Java语言起源始,循序渐进,知识点剖析细致且每章配备大量随堂练习,让你步步为营,学得透彻、练得明白</span><br /><span style="color:#404040;">·拒绝晦涩难懂的呆板教学,宋老师语言生动幽默,举例形象生动深入浅出,迅速让你把握问题本质,四两拨千斤</span><br /><br /><span style="color:#404040;">2.课程内容推陈出新:</span><br /><span style="color:#404040;">·</span><strong>基于JDK 11</strong><span style="color:#404040;">,将Java8、Java9、Java10、Java11新特性一网打尽</span><br /><span style="color:#404040;">·课程中,</span><strong>Eclipse</strong><span style="color:#404040;">和</span><strong>IDEA</strong><span style="color:#404040;">这两种企业一线开发环境都使用到了</span><br /><br /><span style="color:#404040;">3.技术讲解更深入、更全面:</span><br /><span style="color:#404040;">·课程共30天,715个知识视频小节,涉及主流Java使用的方方面面,全而不冗余</span><br /><span style="color:#404040;">·全程内容涵盖</span><strong>数据结构、设计模式、JVM</strong><span style="color:#404040;">内存结构等深度技术</span><br /><span style="color:#404040;">·企业级笔试面试题目深入源码级讲解,拒绝死记硬背</span><br /><br /><span style="color:#404040;">4.代码量更大、案例更丰富、更贴近实战:</span><br /><span style="color:#404040;">·Java语言基础阶段:</span><strong>12720</strong><span style="color:#404040;">行代码,Java语言高级阶段:</span><strong>11684</strong><span style="color:#404040;">行代码</span><br /><span style="color:#404040;">·课堂实战项目</span><strong>3套</strong><span style="color:#404040;">,课后实战项目</span><strong>2套</strong><br /><span style="color:#404040;">·近百道企业面试真题精讲精练、极具实战性</span>
<p> <br /> </p> <p class="ql-long-24357476" style="font-family:"color:#333333;font-size:14px;background-color:#FFFFFF;"> <span style="color:#337FE5;">【课程背景】</span> </p> <p class="ql-long-24357476" style="font-family:"color:#333333;font-size:14px;background-color:#FFFFFF;"> JAVA是市场份额最大的编程语言,每天各大招聘网站上都会有数万个JAVA开发工程师的在招岗位,学习JAVA成为很多人人生逆袭的绝佳工具,但是JAVA的技术体系庞大复杂,要想扎实掌握JAVA不是一件容易的事,随着在线教育的兴起线上学习也成为很多人的选择之一。通过本门课程的学习,可以让系统学习到JAVA相关知识点,轻松进入JAVA领域。 </p> <p> <br /> </p> <p> <span style="color:#337FE5;">【课程收获】</span> </p> <p> 本课程从零开始,以通俗易懂的方式讲解Java入门,手把手教你掌握每一个知识点。  </p> <p> 课程中使用的所有英文单词都会逐一查询并记录,真正做到零基础入门学习,最适合初学者的教程!  </p> <p> <br /> </p> <p> <br /> </p> <p style="font-family:"color:#222226;font-size:14px;background-color:#FFFFFF;"> <span style="font-family:"color:#337FE5;">【课程大纲】</span> </p> <p> <span style="font-family:"color:#222226;font-size:14px;background-color:#FFFFFF;">为了让大家快速系统了解</span>JAVA入门<span style="background-color:#FFFFFF;">知识全貌,我为你总结了「JAVA入门知识框架图」,帮你梳理学习重点,建议收藏!</span> </p> <p> <span style="background-color:#FFFFFF;"><img src="https://img-bss.csdnimg.cn/202007220704529028.png" alt="" /><br /> </span> </p> <p> <br /> </p>
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页