java线程间的协调
比如说在银行开个账户会有一个存折和一张卡,如果某一天同一时间丈夫拿着存折去柜台取钱,而妻子拿着银行卡去ATM取钱。当丈夫查询余额里面有3000元,正准备取2000元,这时候妻子也到ATM里面查询也有3000,也取2000元。其实银行不可能让我们这么做,如果这样的话那我们天天取钱去了,还搞什么工作啊。其实在丈夫查询的时候已经对该账号上了锁,另外的银行卡要取钱的话必须等待该锁被释放。下面用一个程序模拟这个例子:
1 package com.sync; 2 3 public class TestSync2 implements Runnable{ 4 public BankCard bc = new BankCard(); 5 public static void main(String[] args) { 6 TestSync2 test = new TestSync2(); 7 Thread wife = new Thread(test); 8 Thread husband = new Thread(test); 9 wife.setName("wife"); 10 husband.setName("husband"); 11 wife.start(); 12 husband.start(); 13 } 14 public void run() { 15 bc.getMoney(Thread.currentThread().getName(), 2000); 16 } 17 } 18 class BankCard{ 19 private static int money = 3000;//模拟账户余额 20 public synchronized void getMoney(String name,int m){ 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 package com.sync; 2 3 import com.thread.SleepTest; 4 5 6 public class TestDeadLock implements Runnable{ 7 int flag = 1; 8 static Object o1 = new Object(); 9 static Object o2 = new Object(); 10 public void run() { 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 void main(String[] args) { 38 TestDeadLock t1 = new TestDeadLock(); 39 TestDeadLock t2 = new TestDeadLock(); 40 t1.flag = 1; 41 t2.flag = 0; 42 new Thread(t1).start(); 43 new Thread(t2).start(); 44 } 45 }
运行程序输出1 0后就进入死锁状态,该程序永远也不会停止,因为两个线程同时处于等待状态。线程t1锁住了o1对象,等待o2对象,而线程t2锁住o2等待o2对象,谁也不让谁,这就进入了一个循环占有等待的情况了,死锁也就出现了。
所以,如果多个线程如果不进行协调的话很容易出现死锁的问题。操作系统中使用进程调度来协调各个进程,那么java重如何对各个线程进行协调呢?
java中主要使用Object类中的wait()、notify()、notifyAll()方法来协调各个线程。典型的例子有哲学家吃饭问题、生产者和消费者问题、理发师问题。下面一个用一个例子来演示生产者和消费者问题。
问题描述:生产者负责做馒头,做好馒头后放进指定的篓子里面,消费者消费该篓子里面的馒头。篓子里只能装一定量的馒头,满了以后生产者必须进入等待状态,消费者吃完馒头后也必须进入等待状态。
1 package com.sync; 2 3 public class ProductAndConsumer { 4 public static void main(String[] args) { 5 Basket b = new Basket(); 6 Product p = new Product(b); 7 Consumer c = new Consumer(b); 8 new Thread(p).start(); 9 new Thread(c).start(); 10 } 11 } 12 13 class ManTou{ 14 int id; 15 public ManTou(int id) { 16 this.id = id; 17 } 18 @Override 19 public String toString() { 20 return "ManTou"+id; 21 } 22 } 23 24 //装馒头的篮子 25 class Basket{ 26 int index = 0; //相当于栈顶指针 27 ManTou[] manTous = new ManTou[6]; 28 //往篮子里面放馒头 29 public synchronized void push(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 synchronized ManTou 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 return manTous[index]; 53 } 54 } 55 //生产者 56 class Product implements Runnable{ 57 Basket basket; 58 public Product(Basket basket) { 59 this.basket = basket; 60 } 61 public void run() { 62 for (int i = 0; i < 20; i++) { 63 ManTou m = new ManTou(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 implements Runnable{ 78 Basket basket; 79 public Consumer(Basket basket) { 80 this.basket = basket; 81 } 82 public void run() { 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()不同,锁还是归我所有,一段时间后会自动醒过来
java线程间的协调相关推荐
- Java线程间通信-回调的实现方式
2019独角兽企业重金招聘Python工程师标准>>> Java线程间通信-回调的实现方式 Java线程间通信是非常复杂的问题的.线程间通信问题本质上是如何将与线程相关的变量或者对象 ...
- java线程间ThreadLocal的传递
文章目录 1 场景 2 需确认问题 2.1 继承线程的ThreadLocal的含义 2.2 子线程内的ThreadLocal的值和父线程内的有什么关系? 2.3 父线程内的ThreadLocal清除后 ...
- java线程间通信的方式
java线程间通信的方式 1. 共享变量 2. 等待/通知 3. 管道流 1. 共享变量 volatile修饰的变量,线程间可见,可使用这种变量作为线程间传递消息的媒介: 延伸出来的,还有redis中 ...
- Java 线程间的通信机制(等待和唤醒机制)
一. 线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同. 例如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产, ...
- java线程间通信管道_通过管道进行线程间通信
管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据.一个线程发送数据到输出管道,另一个线程从输入管道中读数据.通过管道,实现不同线程间的通信,而无须借助类似共享变量.临时文件之 ...
- JAVA线程间协作:wait.notify.notifyAll
欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...
- java线程间通信:一个小Demo完全搞懂
版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程系列文章只是自己知识的总结梳理,都是最基础的玩意,已经掌握熟练的可以绕过. 一.从一个小Demo说起 上篇我们聊到了Java多线程的同步 ...
- java 线程间通信 handler_Handler不同线程间的通信
转http://www.iteye.com/problems/69457 Activity启动后点击一个界面按钮后会开启一个服务(暂定为padService),在padService中会启动一个线程( ...
- 【JAVA线程间通信技术】
之前的例子都是多个线程执行同一种任务,下面开始讨论多个线程执行不同任务的情况. 举个例子:有个仓库专门存储货物,有的货车专门将货物送往仓库,有的货车则专门将货物拉出仓库,这两种货车的任务不同,而且为了 ...
最新文章
- 磁盘阵列介绍、进程的查看管理、日志文件的查看分析,systemctl的控制
- PyQt5 技术篇-通过参数控制Dialog窗口增加?问号按钮
- 无人值守网络安装Linux
- arcgis人口空间化_数据福利|全球人口密度数据汇总与共享
- Linux rm , cp, mv 命令
- Django REST framework+Vue 打造生鲜超市(九)
- Struts2表单验证的xml配置
- html如何让标签居中显示,HTML怎么让标签居中
- 大脑的扩散磁共振成像(Diffusion MRI)—理论和概念
- 启动虚拟机异常(超完整版)Win10
- 揭秘世界最大对撞机:将破解五大科学谜团(图)
- 转载:《游戏论(一)》——一篇有点意思的文章
- 小武与GPU与pytorch的bug 还有反向传播
- Linux磁盘列阵(RAID),包括RAID详解、Linux磁盘列阵和mdadm命令的相关操作
- 经典同步时序逻辑电路分析汇总(第六道)(同步四进制可逆加减法计数器)
- 学生宿舍管理mysql设计_学生宿舍管理系统的设计与实现(PHP,MySQL)(含录像)
- python图片拼合
- MySql数据查重、去重的实现
- 中国最快的云计算机,中国最快的超级计算机 第一名是神威太湖之光
- 报错:Expected comma jsonc(514)
热门文章
- 关于代码效率提升的方法心路历程(购物车)
- spark-sql createOrReplaceTempView 和createGlobalTempView区别
- 最近项目中遇到的问题以及解决办法
- UVA 11039.Building designing
- windows下面使用nssm设置新的服务实现开机自启等
- BZOJ-1066 蜥蜴 最大流+拆点+超级源超级汇
- iOS之github第三方框架(持续更新)
- Largest Number 179
- 区别Ruby的require,load,和include
- vb添加GIF动态图片