java毕向东学习笔记——day12
来源:互联网 发布:淘宝一淘怎么没有了 编辑:程序博客网 时间:2024/05/18 01:21
一 多线程通讯
- 多线程通讯的意思其实很简单,就是不同于以往的两个线程做同样的事情操作同一个对象——比如day11的火车售票,而是多个线程执行对于同一个对象执行不同的事情,比如说今天的对同一个对象中的数据,一个线程负责输入,一个线程负责输出;
- 多线程通讯由于不同线程做的事情不同,因此需要多个对象实现Runnable接口并重写run方法;
- 多线程应当注意的仍然是安全问题,因此在需要同步的地方慎重判断,本例由于操作的还是同一个对象中的数据,因此两个run方法所在的Input和Output的同步函数可以用操作对象当做锁,从而实现同一把锁,避免安全隐患;
- 今天的代码中要实现的是输入数据,而后立刻输出,因此引入了flag进行判断输入的数据是否已经输出,以及wait方法和notify方法用于暂停/唤醒——输入/输出线程;
- 如果数据已经输出则输入数据,如果数据未输出则暂停当前的输入线程,唤醒输出线程,转到输出线程进行输出,在输出线程中判断数据是否已经输出,如果没有,则输出数据,如果已经输出,则暂停输出线程,唤醒输入线程进行输入;
- 以上的话过于繁琐,还是看两个版本的代码来的快。。。
/* 输入一个姓名和性别,然后用另一个线程输出*/class Resource{ String name; String sex; boolean flag = false; //用来作为判定输入是否输出的标识}class Input implements Runnable{ private Resource res; Input(Resource res){ this.res = res; } public void run(){ int x = 0; while(true){ //两个线程用同一个对象锁 synchronized(res){ if (res.flag) try{ res.wait(); }catch(InterruptedException e){ } if(x==0){ res.name = "张三"; res.sex = "男"; }else { res.name = "WWW"; res.sex = "woman"; } x = (x+1)%2; res.flag = true; res.notify(); } } }}class Output implements Runnable{ private Resource res; Output(Resource res){ this.res = res; } public void run(){ while(true){ //两个线程用同一个对象锁 synchronized(res){ if(!res.flag) try{ res.wait(); }catch(InterruptedException e){ } System.out.println(res.name+"......"+res.sex); res.flag = false; res.notify(); } } }}class ThreadCommunicationDemo{ public static void main(String[] args){ Resource r = new Resource(); Input in = new Input(r); Output out = new Output(r); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); }}
下面的是优化版本
/** 用一个线程输入姓名和性别,用另一个线程输出姓名和性别的 优化版本! @author zck; @version V1.1;*/class Resource{ private String name; private String sex; private boolean flag = false; //用来作为判定输入是否已经输出的标识 public synchronized void setNameSex(String name,String sex){ if (flag){ try{ this.wait(); }catch(InterruptedException e){ } } this.name = name; this.sex = sex; flag = true; notify(); } public synchronized void getNameSex(){ if (!flag){ try{ this.wait(); }catch(InterruptedException e){ } } System.out.println(this.name+"......"+this.sex); flag = false; notify(); }}class Input implements Runnable{ private Resource res; Input(Resource res){ this.res = res; } public void run(){ int x = 0; while(true){ if(x==0){ res.setNameSex("张三","男"); }else { res.setNameSex("WWW","woman"); } x = (x+1)%2; } }}class Output implements Runnable{ private Resource res; Output(Resource res){ this.res = res; } public void run(){ while(true){ res.getNameSex(); } }}class ThreadCommunicationDemo2{ //优化版本 public static void main(String[] args){ Resource r = new Resource(); new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); }}
二 多线程通讯——生产者消费者
- 在多个线程运行时,有多个生产者,多个消费者;
在屏幕上打印输出时,有时候会出现生产两次却只消费了一次的情况,这与预期的生产一次消费一次不符合,这种安全隐患来自于flag的if判断,因为只判定一次,当生产线程1通过if判断时,执行完wait语句就失去了执行权,然后接下来消费线程1执行,然后生产线程2执行(因为此时flag是false),当生产线程2执行完时,flag变成了true,但是由于生产线程1已经在之前中通过了if判断,当生产线程1获得执行权时,就会再生产一次;
打印结果:
生产商品1
消费商品1
生产商品2
生产商品3
消费商品3
改进方法就是将if换成while判断还有一种安全隐患就是notify叫醒的线程池里本类线程,就会出现叫醒生产线程1叫醒生产线程2,然后所有线程都挂了(都进入wait状态);处理方式就是改notify为notifyAll,干脆全叫醒,就不会出现所有线程一起挂在那儿的情况了;
class ProducerConsumerDemo{ public static void main(String[] args){ ProducerConsumer pc = new ProducerConsumer(); Producer pro = new Producer(pc); Consumer con = new Consumer(pc); Thread t1 = new Thread(pro); Thread t2 = new Thread(pro); Thread t3 = new Thread(con); Thread t4 = new Thread(con); t1.start(); t2.start(); t3.start(); t4.start(); }}class ProducerConsumer{ private String name; private int count = 1; private boolean flag = false; public synchronized void setName(String name){ while(true){ while(flag){ try{ wait(); }catch(InterruptedException e){ } } this.name = name+"..."+count++; System.out.println(Thread.currentThread().getName()+"---生产---"+this.name); flag = true; this.notifyAll(); } } public synchronized void getName(){ while(true){ while(!flag){ try{ wait(); }catch(InterruptedException e){ } } System.out.println(Thread.currentThread().getName()+"---消费-----"+this.name); flag = false; this.notifyAll(); } }}class Producer implements Runnable{ ProducerConsumer pc; Producer(ProducerConsumer pc){ this.pc = pc; } public void run(){ pc.setName("商品"); }}class Consumer implements Runnable{ ProducerConsumer pc; Consumer(ProducerConsumer pc){ this.pc = pc; } public void run(){ pc.getName(); }}
下面是java5.0改进版本
1. 引入了lock代替synchronized,condition对象的await代替wait,signal和signalAll代替notify和notifyAll;
import java.util.concurrent.locks.*;class ProducerConsumerDemo2{ public static void main(String[] args){ ProducerConsumer pc = new ProducerConsumer(); Producer pro = new Producer(pc); Consumer con = new Consumer(pc); Thread t1 = new Thread(pro); Thread t2 = new Thread(pro); Thread t3 = new Thread(con); Thread t4 = new Thread(con); t1.start(); t2.start(); t3.start(); t4.start(); }}class ProducerConsumer{ private String name; private int count = 1; private boolean flag = false; private Lock lock = new ReentrantLock(); private Condition condition_pro = lock.newCondition(); private Condition condition_con = lock.newCondition(); public void setName(String name) throws InterruptedException{ lock.lock(); try{ while(flag){ condition_pro.await(); } this.name = name+"..."+count++; System.out.println(Thread.currentThread().getName()+"---生产---"+this.name); flag = true; condition_con.signalAll(); }finally{ lock.unlock(); } } public void getName() throws InterruptedException{ lock.lock(); try{ while(!flag){ condition_con.await(); } System.out.println(Thread.currentThread().getName()+"---消费-----"+this.name); flag = false; condition_pro.signalAll(); }finally{ lock.unlock(); } }}class Producer implements Runnable{ ProducerConsumer pc; Producer(ProducerConsumer pc){ this.pc = pc; } public void run(){ while(true){ try{ pc.setName("商品"); }catch(InterruptedException e){ } } }}class Consumer implements Runnable{ ProducerConsumer pc; Consumer(ProducerConsumer pc){ this.pc = pc; } public void run(){ while(true){ try{ pc.getName(); }catch(InterruptedException e){ } } }}
三 中断线程
- 有时候线程处于冻结状态后无法正常读取唤醒标记,这时候就需要外力强制中断线程的冻结状态;
- Thread类就提供了interrupt方法,强制唤醒线程(好比把被催眠的人一砖头砸醒),但是这样会导致wait或者sleep抛出异常(因为被砖头砸醒不比正常唤醒,会受伤的,,);
class InterruptThreadDemo{ public static void main(String[] args){ int num = 0; TestThread tt = new TestThread(); Thread t1 = new Thread(tt); Thread t2 = new Thread(tt); t1.start(); t2.start(); while(true){ if(num++ == 60){ //tt.changeFlag(); //强制唤醒了t1和t2线程 t1.interrupt(); t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"..."+num); } }}class TestThread implements Runnable{ private boolean flag = true; public synchronized void run(){ while(flag){ try{ wait(); }catch(InterruptedException e){ System.out.println(Thread.currentThread().getName()+"InterruptedException"); //结束循环实则为结束进程 flag = false; } System.out.println(Thread.currentThread().getName()+"run....."); } } public void changeFlag(){ flag = false; }}
四 守护线程
- 可以通过Thread 的setDaemon方法将一些线程设置为守护线程;
- 守护线程的设置应当在线程执行前;
- 守护线程可视为后台线程,当所有前台线程都结束时,守护线程自动结束;
- 这意味着守护线程即使被wait了,也没必要唤醒后再结束,只要前台线程跑完即可;
class InterruptThreadDemo{ public static void main(String[] args){ int num = 0; TestThread tt = new TestThread(); Thread t1 = new Thread(tt); Thread t2 = new Thread(tt); t1.setDaemon(true); t2.setDaemon(true); t1.start(); t2.start(); while(true){ if(num++ == 60){ //tt.changeFlag(); //强制唤醒了t1和t2线程 //t1.interrupt(); //t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"..."+num); } }}class TestThread implements Runnable{ private boolean flag = true; public void run(){ while(flag){ System.out.println(Thread.currentThread().getName()+"run....."); } } public void changeFlag(){ flag = false; }}
五 join、yield方法和优先级
- join方法实则为暂停当前线程,优先执行调用join方法的线程,直到调用join方法的线程结束后再执行当前线程;
- toString方法可以查看线程名称,优先级,以及所在线程组;
- 优先级为1——10,代表被cpu执行的概率;
- 通过setPriority(Thread.MAX/MIN/NORM_PRIORITY)设置三个不同的优先级,对应10/1/5;
- yield方法是暂停当前线程,执行其他线程;
class JoinDemo{ public static void main(String[] args) throws InterruptedException{ Join jo = new Join(); Thread t1 = new Thread(jo); Thread t2 = new Thread(jo); t1.start(); t1.setPriority(Thread.MAX_PRIORITY); t2.start(); //t1.join(); for(int x = 0;x<80;x++){ System.out.println(Thread.currentThread().toString()+"....."+x); } }}class Join implements Runnable{ public void run(){ for (int x = 0;x<70;x++){ System.out.println(Thread.currentThread().toString()+"..."+x); //Thread.yield(); } }}
阅读全文
0 0
- java毕向东学习笔记——day12
- java学习笔记——毕向东视频day01
- java学习笔记——毕向东视频day02
- java学习笔记——毕向东视频day03
- java学习笔记——毕向东视频day04
- java学习笔记——毕向东视频day05
- java学习笔记——毕向东视频day06
- 异常处理——毕向东Java基础教程学习笔记
- java毕向东学习笔记——day01
- java毕向东学习笔记——day02~day05
- java毕向东学习笔记——day06
- java毕向东学习笔记——day07
- java毕向东学习笔记——day08
- java毕向东学习笔记——day09
- java毕向东学习笔记——day10
- 毕向东java学习笔记
- Java毕向东01——笔记
- Java 学习笔记——线程间通信(day12)
- 小福利 —— 实时更新的页面小时钟
- LintCode 68.二叉树的前序遍历
- bootstrap怎么设置下拉菜单不点击,改成鼠标悬停直接显示下拉菜单
- Python初学——窗口视窗Tkinter
- AT24C32读取数据一直为0xFF
- java毕向东学习笔记——day12
- BZOJ 1834: [ZJOI2010]network 网络扩容
- static关键字-内存图解
- 深入理解JVM(七)——性能监控工具
- Tr A HDU
- Java线程池之ThreadPoolExecutor
- 鱼塘钓鱼
- 深入理解Java虚拟机(第六章):类文件结构
- html-css表单的margin-right/padding-right属性设置无效解决办法