java笔记15 多线程2(线程通信、Lock)
来源:互联网 发布:星尘浏览器知乎 编辑:程序博客网 时间:2024/05/17 21:41
1. 线程间通信
1.1 意义:多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。
此时输入输出都要上锁,而且要保证是同一个锁。
1.2 等待/唤醒机制涉及的方法:
1、wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
2、notify():唤醒线程池中的一个线程(任何一个都有可能)。
3、notifyAll():唤醒线程池中的所有线程。
1.3注意:
1、这些方法都需要定义在同步中。
2、这些方法必须要标示所属的锁。
A锁上的线程被wait了,那这个线程就相当于处于A锁的线程池中,只能A锁的notify唤醒。
3、这三个方法都定义在Object类中。为什么操作线程的方法定义在Object类中?
因为这三个方法都需要定义同步内,并标示所属的同步锁,锁可以是任意对象,那么能被任意对象调用的方法一定定义在Object类中。
1.4wait和sleep区别:
wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。
sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
wait:线程会释放执行权,而且线程会释放锁。
sleep:线程会释放执行权,但不是不释放锁。
wait是定义在Object中的方法,sleep是线程中的方法。
2. 生产者消费者示例
public class H_04ThreadWaitNotify{ public static void main(String[] args) { Reso r=new Reso();//创建资源对象 new Thread(new Input(r)).start();//匿名创建线程 new Thread(new Output(r)).start();//匿名创建线程 }}class Reso{ private Stringname; private Stringsex; private boolean flag=false; public synchronized void set(String name,String sex)//锁是this { if(this.flag) try { this.wait();//使用时要指定锁 } catch (InterruptedExceptione) { e.printStackTrace(); } this.name=name; this.sex=sex; this.flag=true; this.notify(); } public synchronized void out()//锁是this { if(!this.flag) try { this.wait(); } catch (InterruptedExceptione) { e.printStackTrace(); } System.out.println(name+"......."+sex); this.flag=false; this.notify(); } } class Input implements Runnable{ private Reso r; Input(Reso r) { this.r=r; } public void run() { int x=0; while(true) { if (x==0) r.set("mike","man"); else r.set("lili","女"); x=(x+1)%2; } }} class Output implements Runnable{ private Reso r; Output(Reso r) { this.r=r; } public void run() { while(true) { r.out(); } }}
结果(不停打印)
mike.......man
lili.......女
mike.......man
lili.......女
3. 多生产者—消费者问题
代码
public class H_05ProductDemo{ public static void main(String [] args) { Resouce r=new Resouce(); new Thread(new Producer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Consumer(r)).start(); }}class Resouce{ private String name; private int count=1; private boolean flag=false; public synchronized void set (String name) { if(flag) { try{this.wait();} catch(Exceptione){} } this.name=name+"---"+count++;//名字+编号 System.out.println(Thread.currentThread().getName()+"_______....生产者..._______"+this.name); flag=true; this.notify(); } public synchronized void out () { if(!flag) { try{this.wait();} catch(Exceptione){} } System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name); flag=false; this.notify(); } }class Producer implements Runnable//生产者调用set方法{ private Resouce res; Producer(Resouce res) { this.res=res; } public void run() { while(true) { res.set("+商品+"); } }} class Consumer implements Runnable//消费者调用out方法{ private Resouce res; Consumer(Resouce res) { this.res=res; } public void run() { while(true) { res.out(); } }}
部分结果
Thread-2_______....生产者..._______+商品+---66093Thread-0_______....生产者..._______+商品+---66094Thread-3...消费者...+商品+---66094Thread-2_______....生产者..._______+商品+---66095Thread-0_______....生产者..._______+商品+---66096Thread-3...消费者...+商品+---66096Thread-2_______....生产者..._______+商品+---66097Thread-0_______....生产者..._______+商品+---66098Thread-3...消费者...+商品+---66098
线程2生产了66093,flag设为true,2继续执行,2等待。分析:(0、2生产,1、3消费)
线程0(之前等待)生产了66094,flag设为true,线程0等待。
线程3获得了执行权,消费了66094,flag设为false,3等待,唤醒线程2,
线程2无需判断,生产了66095,flag为true,线程2等待,并唤醒了线程0.
线程0也无需判断,生产了66096,flag为true,线程0等待,唤醒了线程3.
原因:
由于if判断,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况。故修改成while判断,线程获取CPU执行权及锁后,将重新判断是否具备运行条件。
notify方法只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。notifyAll解决了本方线程一定会唤醒对方线程的问题。
while+notify的死锁分析
线程0生产了。001,flag为true,0继续执行,0等待
线程2判断,flag为true,2等待。
线程1获取了执行权和锁,消费了001,flag设为false,线程1等待,唤醒了线程0,但是线程0无锁。
线程3执行,flag为false,线程3等待。此时1、2、3均等待。
线程0获得了锁,生产了002,flag设为true,唤醒了2,但flag为true,所以2等待。0继续执行,0等待。
此时所有线程都在等待,形成死锁。
故应该使用while+notifyAll,每次判断并唤醒其他所有线程,避免数据出错和死锁。
4. Lock和Condition
jdk1.5 关于多线程升级解决方案:
4.1 将同步synchronized替换成了显式Lock操作。
4.2 将Object中的wait,notify,notifyAll,替换成了condition对象。
4.3 Condition对象可以通过Lock锁获取。
4.4 同一个锁创建不同的Condition对象,同一个Condition对象分别在a方法中等待,在b方法中唤醒。
这样可以实现在本方法中唤醒对方操作,解决了notify可能唤醒本方操作无意义的问题。
注意:释放锁动作一定要执行,要放在finally中。
替换后的主要代码:
class Resouce2 { private Stringname; private int count=1; private boolean flag=false; private Locklock=new ReentrantLock();//创建一个锁//Lock是接口,所以用子类创建 private Conditioncondition_pro=lock.newCondition();//创建不同的对象 private Conditioncondition_con=lock.newCondition(); public void set (String name) { lock.lock();//显式加锁 try { while(flag) condition_pro.await();//通过Condition对象调用 this.name=name+"---"+count++; System.out.println(Thread.currentThread().getName()+"_______....生产者..._______"+this.name); flag=true; condition_con.signal();//唤醒对方线程(与con对应) } catch (InterruptedExceptione) { e.printStackTrace(); } finally { lock.unlock();//一定要释放锁 } } public void out () { lock.lock(); try { while(!flag) condition_con.await(); System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name); flag=false; condition_pro.signal();//唤醒对方 } catch (InterruptedExceptione) { e.printStackTrace(); } finally { lock.unlock(); } } }
替换方式:
建立锁对象
建立不同的Condition对象
去掉原有的synchronized
使用Condition对象的wait方法
使用另一个Condition对象的signal方法。
释放锁。
5. 线程的停止
5.1 stop方法已经过时。
5.2 如何停止线程?
只有一种方法:run方法结束,
开启多线程运行,运行代码通常是循环结构,
只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于冻结状态,就不会读取标记,那么线程就不会结束。
5.3 清除冻结状态
当没有指定的方式让冻结的线程恢复到运行状态时,这是需要对冻结状态进行清除。
强制让线程恢复到运行状态汇总来,这样就可以操作标记让线程结束。
Thread提供了interrupt方法。
public class H_07StopThread{ public static void main(String[] args) { StopTest st=new StopTest(); Thread t1=new Thread(st); Thread t2=new Thread(st); t1.start();//wait t2.start();//wait intnum=0; while(true) { if(num++==60)//主线程运行到num=60 main...60 { t1.interrupt();//打断wait运行Thread-0...run t2.interrupt();//打断wait运行Thread-1...run break; } System.out.println(Thread.currentThread().getName()+"......"+num); } }} class StopTest implements Runnable{ private boolean flag=true; public synchronized void run() { while(flag) { try { wait(); } catch (InterruptedExceptione) { flag=false;//在catch中处理 } System.out.println(Thread.currentThread().getName()+".....run"); } } public void changeflag() { flag=false; }}
6. 多线程的其他方法
6.1 setDaemon(true):将该线程标记为守护线程或用户线程。将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
Stop st=new Stop(); Thread t1=new Thread(st); Thread t2=new Thread(st); t1.setDaemon(true);//运行前执行,当只剩下守护线程时,jvm退出 t2.setDaemon(true); t1.start(); t2.start(); intnum=0; while(true)//当主线程运行结束,只剩下守护线程,jvm退出 { if(num++==60) { st.changeflag(); break; } System.out.println(Thread.currentThread().getName()+"......"+num); }
6.2 join:临时加入一个线程的时候可以使用join方法。
当A线程执行到了B线程的join方式。A线程处于冻结状态,释放了执行权,B开始执行。A什么时候执行呢?只有当B线程运行结束后,A才从冻结状态恢复运行状态执行。
6.3 Thread.yield():暂停当前正在执行的线程对象,并执行其他线程。
setPriority(int newPriority):更改线程的优先级。
getPriority():返回线程的优先级。(默认是5)
public class H_09JoinThreadDemo{ public static void main(String[] args) throws Exception { Demo d=new Demo(); Thread t1=new Thread(d); Thread t2=new Thread(d); t1.start(); t1.join();//t1强制获得执行权 //t1.setPriority(Thread.MAX_PRIORITY);//静态常量 t2.start(); t2.setPriority(Thread.MIN_PRIORITY); //t1.join(); for(inti=0;i<80;i++)//主线程在执行 { System.out.println(Thread.currentThread().toString()+"...."+i); } }}class Demo implements Runnable{ public void run() { for(inti=0;i<70;i++) { System.out.println(Thread.currentThread().toString()+"...."+i); Thread.yield();//每次运行就切换到另外一个线程执行 } }}
结果:
线程0先执行完(强制插入)
线程1和main线程穿插执行(线程1的优先级显示为1)
- java笔记15 多线程2(线程通信、Lock)
- java笔记-多线程-线程通信
- JAVA多线程—Lock&Condition实现线程同步通信
- java基础-5-多线程(2)-死锁与线程间通信(synchronized与Lock的区别及各自用法)
- 多线程(线程安全、线程间通信、1.5中的Lock)总结2
- Java多线程编程-(5)-使用Lock对象实现同步以及线程间通信
- 【java】同步,线程通信,lock
- (六) Java多线程详解之线程锁Lock和Condition线程通信技术
- Java 多线程(三)线程间的通信jdk1.5中Lock,Condition---生产者消费者为例
- Java 多线程(四)线程间的通信jdk1.5中Lock,Condition----各种锁的相关详细概念
- java多线程线程通信
- Java 多线程:线程通信
- Java笔记3 多线程<2>线程间通信-代码分析以及多线程常见方法的运用
- Java多线程与并发应用-(9)-锁lock+条件阻塞conditon实现线程同步通信
- 黑马程序员——Java多线程线程间通信之Lock的应用
- java多线程通信之lock和Condition
- (笔记九)多线程、Lock、停止线程
- java 线程通信Lock和condition接口
- 修改Java文件不用重启Tomcat服务
- 2015061507 - 注释说明
- 微软云虚拟机外网访问
- Python - VS Complier
- C#数据绑定
- java笔记15 多线程2(线程通信、Lock)
- 2015061508 - 注释分析(1)
- 开始写第一个android软件(聊天软件)
- Mysql修改字符集
- 2015061509 - 注释分析(2)
- 2015年下半年信息系统项目管理师课程安排表
- Notifications 通知
- 6月15日所得所感
- 深入学习横竖屏切换时候Activity的生命周期