多线程(三)--多线程间通信
来源:互联网 发布:2015淘宝双十一销售额 编辑:程序博客网 时间:2024/06/03 16:40
一.线程间通信
1.生产者与消费者问题
//多线程经典,卖烤鸭程序--生产者与消费者//定义一个资源类Resourceclass Resource{ private String name; private int count = 1; //set()方法给生产者调用 public void set(String name){ this.name = name + count; //调用set()方法count自增 count++; System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name); } //out()方法给消费者调用 public void out(){ System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name); }}class Producer implements Runnable{ private Resource r; Producer(Resource r){ this.r = r; } public void run(){ //设置生产者不停地生产 while(true){ r.set("烤鸭"); } }}class Consumer implements Runnable{ private Resource r; Consumer(Resource r){ this.r = r; } public void run(){ //设置消费者不停地消费 while(true){ r.out(); } }}class ProducerConsumerDemo{ public static void main(String[] args) { Resource r = new Resource(); //创建一个生产者和一个消费者 Producer pro = new Producer(r); Consumer con = new Consumer(r); //分别将其加入到两个线程中 Thread t0 = new Thread(pro); Thread t1 = new Thread(con); t0.start(); t1.start(); }}
运行结果:
问题:消费者消费了生产者没有生产的资源
分析:set( )方法内操作了共享数据count,而且有多条操作语句,出现了多线程安全问题,消费者的线程可能抢在生产者前打印数据(如图烤鸭1638)
解决:使用同步函数解决多线程安全问题
只需更改Resource类,将set( )函数和out( )函数加上synchronized关键字
class Resource{ private String name; private int count = 1; public synchronized voidset(String name){ this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name); } public void synchronized out(){ System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name); }}
运行结果:
问题:消费者只能消费最近一次生产的资源
原因:两个方法使用了同一个锁,同一时间只能有一个方法执行,而set( )方法执行时count不断自增,out( )方法执行时count值则一直不变
显然仅仅加一个锁是不能达到我们的预期的,这时就要考虑其他解决办法。即线程间通信
2.线程间通信的概念
多个线程在操作同一个资源,但是操作的动作却不一样
实现方式:
1:将资源封装成对象
2:将线程执行的任务(任务其实就是run方法。)也封装成对象
3.等待唤醒机制:
涉及到的方法:
wait( )
将同步中的线程处于冻结状态,释放了执行权和执行资格,同时将线程对象存储到线程池中
notify( )
唤醒线程池中某一个等待线程
notifyAll( )
唤醒线程池中的所有线程
使用前提:
1,这些方法都需要定义在同步中,不使用同步则这些方法没有意义
2,这些方法必须要标示所属的锁。
A锁上的线程被wait了,那这个线程就会被冻结然后保存到A锁的线程池中,只能A锁的notify唤醒
这三个方法都定义在Object类中,因为这三个方法在使用过程中必须要标识所属的锁对象,而锁对象可以是任意对象,所以这些可以被任意对象调用的方法一定定义在Object类中
//使用等待唤醒机制解决生产者和消费者问题class Resource{ private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name){ //如果flag=true,则生产者进入等待状态 while(flag) try{this.wait();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name); flag = true; //执行完后唤醒消费者 this.notify(); } public synchronized void out(){ //如果flag=false,则消费者进入等待状态 while(!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name); flag = false; //执行完后唤醒生产者 this.notify(); }}class Producer implements Runnable{ private Resource r; Producer(Resource r){ this.r = r; } public void run(){ while(true){ r.set("烤鸭"); } }}class Consumer implements Runnable{ private Resource r; Consumer(Resource r){ this.r = r; } public void run(){ while(true){ r.out(); } }}class ProducerConsumerDemo{ public static void main(String[] args){ Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(con); t0.start(); t1.start(); }}
执行结果
wait和sleep区别:
1.唤醒方式不同
wait:可以指定时间也可以不指定时间,不指定时间则只能由对应的notify或者notifyAll来唤醒。
sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
2.线程持有的锁的释放
wait:线程会释放执行权,而且线程会释放锁。
Sleep:线程会释放执行权,但是不释放锁。
4.多生产多消费
public class ProducerConsumerDemo { public static void main(String[] args){ Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); //分别定义两个生产者和两个消费者 Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); }}
定义多个生产者和消费者
运行结果:
现象:程序停止不动,不在继续向下执行,但是却不是死锁,因为程序中没有同步的嵌套
分析:set( )和get( )方法中唤醒线程使用的是notify( )方法,只能唤醒一个线程。所以可能出现一个生产者线程唤醒了另一个生产者线程,而flag值是true,被唤醒的这个生产者线程经过while判断后也进入了wait等待状态,然后程序中就没有可以执行的线程了,程序停止。
解决:使用notifyAll( )方法,将所有线程都唤醒
class Resource{ private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name){ while(flag) try{this.wait();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name); flag = true; //只需将notify更改为notifyAll this.notifyAll(); } public synchronized void out(){ while(!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name); flag = false; this.notifyAll(); }}
二.线程的停止
1.使用stop方法(已过时)
2.结束run( )方法
run方法里一般会定义循环,所以只要结束循环即可。
第一种方式:定义循环的结束标记。
第二种方式:如果线程处于了冻结状态,是不可能读到标记的,这时就需要通过Thread类中的interrupt方法,将其冻结状态强制清除。让线程恢复具备执行资格的状态,让线程可以读到标记,并结束。
三.Lock接口和Condition接口
解决线程安全问题使用同步的形式(同步代码块和同步函数),其实最终使用的都是锁机制。线程进入同步就是具备了锁,执行完毕离开同步,就是释放了锁。获取锁,释放锁的动作是锁对象的内容。在面向对象思想的指导下,将锁单独定义为对象并封装对锁的操作。Java的设计者专门开发了一个Lock接口,将获取锁,释放锁等操作都定义到了这个接口中
同步是隐示的锁操作,而Lock接口是显示的锁操作,Lock接口的出现就替代了同步。
在之前的版本中使用Object类中wait、notify、notifyAll的方式来完成的。那是因为同步中的锁是任意对象,所以操作锁的等待唤醒的方法都定义在Object类中。
而现在锁是指定对象Lock。所以查找等待唤醒机制方式需要通过Lock接口来完成。而Lock接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了Condition接口中。
Condition接口将Object中的三个方法进行单独的封装。并提供了功能一致的方法 await( )、signal( )、signalAll( )
使用Lock接口实现多生产多消费
import java.util.concurrent.locks.*;class Resource{ private String name; private int count = 1; private boolean flag = false; //创建一个Lock接口的实现类ReentrantLock的对象 Lock lock = new ReentrantLock(); //通过lock锁创建两个监听器,分别监听生产者和消费者 Condition producer_con = lock.newCondition(); Condition consumer_con = lock.newCondition(); public void set(String name){ //获取锁(相当于加同步) lock.lock(); try{ while(flag) //监听器调用await()方法使线程进入等待状态 try{producer_con.await();}catch(InterruptedException e){} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"...生产者5.0..."+this.name); flag = true; //唤醒消费者的线程 consumer_con.signal(); } finally{ //操作执行完毕,释放锁 lock.unlock(); } } public void out(){ lock.lock(); try{ while(!flag) try{cousumer_con.await();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+"...消费者.5.0......."+this.name); flag = false; producer_con.signal(); } finally{ lock.unlock(); } }}class Producer implements Runnable{ private Resource r; Producer(Resource r){ this.r = r; } public void run(){ while(true){ r.set("烤鸭"); } }}class Consumer implements Runnable{ private Resource r; Consumer(Resource r){ this.r = r; } public void run(){ while(true){ r.out(); } }}class ProducerConsumerDemo2{ public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); }}
- 多线程(三)--多线程间通信
- java多线程(三)线程间通信
- 多线程(三)--线程间通信
- 多线程编程--多线程间通信(五)
- C++多线程编程(三)线程间通信
- 多线程编程核心技术读书笔记(三):进程间通信
- (转载)多线程编程学习三(线程间通信)
- C++多线程编程 (三) 线程间通信
- 多线程入门教程三:线程间通信
- Java多线程核心技术(三):线程间通信
- 多线程编程学习三(线程间通信)
- 多线程之三 线程通信
- 多线程线程间通信
- javaSE-多线程间通信
- MFC 多线程间通信
- qt多线程间通信
- 多线程间的通信
- Android多线程间通信
- linux命令简单介绍以及使用
- 【Elasticsearch】1环境
- 百分比布局
- JavaScript的NaN为什么不等于NaN
- python之if语句
- 多线程(三)--多线程间通信
- codevs 3732 NOIP2014 解方程 解题报告
- For Fun:使用 IDA 调试 iOS 程序的步骤
- MySql日期处理函数
- 基于TCP协议的Socket通信(文件传输且续传)
- Daily Meeting Principe
- oa项目心得
- Spring Boot 网易云课堂视频【第八章 Spring Boot 知识点1】
- Cocoapods安装详细流程