黑马程序员——Java基础---线程间通信
2015-06-08 16:08
549 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
当前的线程必须拥有当前对象的monitor,也即lock,就是锁。
线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。
要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。
如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是由于调用了wait方法中的一个)。
被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁。
被唤醒的线程将和其他线程以通常的方式进行竞争,来获得对象的锁。也就是说,被唤醒的线程并没有什么优先权,也没有什么劣势,对象的下一个线程还是需要通过一般性的竞争。
notify()方法应该是被拥有对象的锁的线程所调用。
wait()和notify()方法要求在调用时线程已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或synchronized块中。
一个线程变为一个对象的锁的拥有者是通过下列三种方法:
1.执行这个对象的synchronized实例方法。
2.执行这个对象的synchronized语句块。这个语句块锁的是这个对象。
3.对于Class类的对象,执行那个类的synchronized、static方法。
代码示例1:
代码示例:
代码示例:
因为线程运行代码一般都是循环,只要控制了循环即 可。
2.使用interrupt(中断)方法。
该方法是结束线程的冻结状态,使线程回到 运行状态中来。
注:stop方法已经过时不再使用。
代码示例:
l setPriority(int num)
l setDaemon(boolean b)
l join()
l 自定义线程名称l
l toString()
代码示例:
代码示例:
总结
线程之间的通信,是日常生活当中经常会遇见的,而在多个线程之间对抢夺cpu执行权的时候要格外小心,用的不好可能导致程序无法继续运行,或者是使数据错乱。在学习这方面的知识时,还需要更多的去回顾并对知识点深度的理解。
线程间的相互作用
线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务。wait()方法
wait()方法使得当前线程必须要等待,等到另外一个线程调用notify()或者notifyAll()方法。当前的线程必须拥有当前对象的monitor,也即lock,就是锁。
线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。
要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。
notify()方法
notify()方法会唤醒一个等待当前对象的锁的线程。如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是由于调用了wait方法中的一个)。
被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁。
被唤醒的线程将和其他线程以通常的方式进行竞争,来获得对象的锁。也就是说,被唤醒的线程并没有什么优先权,也没有什么劣势,对象的下一个线程还是需要通过一般性的竞争。
notify()方法应该是被拥有对象的锁的线程所调用。
wait()和notify()方法要求在调用时线程已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或synchronized块中。
一个线程变为一个对象的锁的拥有者是通过下列三种方法:
1.执行这个对象的synchronized实例方法。
2.执行这个对象的synchronized语句块。这个语句块锁的是这个对象。
3.对于Class类的对象,执行那个类的synchronized、static方法。
代码示例1:
/* 线程间通讯: 其实就是多个线程在操作同一个资源。 但是操作的动作不同。 wait(); notify(); notifyAll(); 都使用在同步中,因为要对只有监视器(锁)的线程操作。 所以要使用在同步中,因为只有同步才具有锁。 为什么这些操作线程的方法要定义Object类中呢? 因为这些方法在操作同步线程时,都必须要标识他们所操作线程持有的锁。 只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。 不可以对不同锁中的线程进行唤醒。 也就是说,等待和唤醒必须是同一个锁。 而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。 */ class Res { String name; String sex; boolean flag = false; } class Input implements Runnable { private Res r; Input(Res r) { this.r = r; } public void run() { int x = 0; while(true) { synchronized(r) { if(r.flag) try{r.wait();}catch(Exception e){} if(x==0) { r.name="mike"; r.sex = "man"; } else { r.name="丽丽"; r.sex="女女女女女"; } x=(x+1)%2; r.flag = true; r.notify(); } } } } class Output implements Runnable { private Res r; Output(Res r) { this.r = r; } public void run() { while(true) { synchronized(r) { if(!r.flag) try{r.wait();}catch(Exception e){} System.out.println(r.name+"..."+r.sex); r.flag = false; r.notify(); } } } } class InputOutputDemo { public static void main(String[] args) { Res r = new Res(); Input in = new Input(r); Output out = new Output(r); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); } }代码示例2:
class Res { private String name; private String sex; private boolean flag = false; public synchronized void set(String name,String sex) { if(flag) try{this.wait();}catch(Exception e){} this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void out() { if(!flag) try{this.wait();}catch(Exception e){} System.out.println(name+"....."+sex); flag = false; this.notify(); } } class Input implements Runnable { private Res r; Input(Res r) { this.r = r; } public void run() { int x = 0; while(true) { if(x==0) r.set("mike","man"); else r.set("丽丽","女女女女女"); x=(x+1)%2; } } } class Output implements Runnable { private Res r; Output(Res r) { this.r = r; } public void run() { while(true) { r.out(); } } } class InputOutputDemo2 { public static void main(String[] args) { Res r = new Res(); new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); /* Input in = new Input(r); Output out = new Output(r); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); */ } }
生产者消费者问题
等待唤醒机制代码示例:
/* 对于多个生产者和消费者。 为什么要定义while判断标记? 原因:让被唤醒的线程再一次判断标记。 为什么定义notifyAll? 因为需要唤醒对方线程。 因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。 */ class Resource { private String name; private int count=1; private boolean flag=false; public synchronized void set(String name) { while(flag) try{wait();}catch(Exception e){} this.name=name+"..."+count++; System.out.println(Thread.currentThread().getName()+".....生产者....."+ 4000 this.name); flag=true; this.notifyAll(); } public synchronized void out() { while(!flag) try{wait();}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"消费者"+this.name); flag=false; this.notifyAll(); } } class Producer implements Runnable { private Resource res; Producer(Resource res) { this.res=res; } public void run() { while(true) { res.set("+商品+"); } } } class Consumer implements Runnable { private Resource res; Consumer(Resource res) { this.res=res; } public void run() { while(true) { res.out(); } } } class ProducerConsumerDemo { public static void main(String[] args) { Resource r=new Resource(); Producer pro =new Producer(r); Consumer con =new Consumer(r); 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(); } }经过JDK1.5升级之后的等待唤醒机制
代码示例:
/* JDK1.5中提供了多线程升级解决方案。 将同步synchronized替换成显式Lock操作。 将Object中的wait,notify,notifyAll,替换了Condition对象。 该对象可以Lock锁进行获取。 在该示例中,实现了本方只唤醒对方操作。 */ import java.util.concurrent.locks.*; class Resource { 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 set(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.signal(); }finally { lock.unlock();//施放锁一定要执行。 } } public void out()throws InterruptedException { lock.lock(); try { while(!flag) condition_con.await(); System.out.println(Thread.currentThread().getName()+"消费者"+this.name); flag=false; condition_pro.signal(); } finally { lock.unlock(); } } } class Producer implements Runnable { private Resource res; Producer(Resource res) { this.res=res; } public void run() { while(true) { try{res.set("+商品+");}catch(InterruptedException e){} } } } class Consumer implements Runnable { private Resource res; Consumer(Resource res) { this.res=res; } public void run() { while(true) { try{res.out();}catch(InterruptedException e){} } } } class ProducerConsumerDemo2 { public static void main(String[] args) { Resource r=new Resource(); Producer pro =new Producer(r); Consumer con =new Consumer(r); 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(); } }
停止线程
1.定义循环结束标记因为线程运行代码一般都是循环,只要控制了循环即 可。
2.使用interrupt(中断)方法。
该方法是结束线程的冻结状态,使线程回到 运行状态中来。
注:stop方法已经过时不再使用。
代码示例:
/* stop方法已经过时。 如何停止线程? 只有一种,run方法结束。 开启多线程运行,运行代码通常是循环结构。 只要控制循环,就可以让run方法结束,也就是线程结束。 特殊情况: 当线程处于冻结状态 就不会读取到标记。那么线程就不会结束。 当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结线程进行清除。 强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。 Thread类提供该方法interrupt(); */ class StopThread implements Runnable { private boolean flag = true; public synchronized void run() { while(flag) { /* try { wait(); } catch(InterruptedException e) { System.out.println(Thread.currentThread().getName()+"...Exception"); flag = false; } */ System.out.println(Thread.currentThread().getName()+"...run"); } } public void changeFlag() { flag = false; } } class StopThreadDemo { public static void main(String[] args) { StopThread s = new StopThread(); Thread t1 = new Thread(s); Thread t2 = new Thread(s); t1.setDaemon(true); t2.setDaemon(true); t1.start(); t2.start(); int num = 0; while(true) { if(num++ == 60) { //s.changeFlag(); //t1.interrupt(); //t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"....."+num); } System.out.println("over"); } }
线程类的其他方法
ll setPriority(int num)
l setDaemon(boolean b)
l join()
l 自定义线程名称l
l toString()
代码示例:
/* join: 当A线程执行到了B线程的join()方法时,A线程就会等待,等B线程都执行完,A才会执行 join可以用来临时加入线程执行。 */ class Demo implements Runnable { public void run() { for(int x=0;x<70;x++) { System.out.println(Thread.currentThread().toString()+"..."+x); Thread.yield(); } } } class JoinDemo { public static void main(String[] args)throws InterruptedException { Demo d = new Demo(); Thread t1 = new Thread(d); Thread t2 = new Thread(d); t1.start(); t1.setPriority(Thread.MAX_PRIORITY); //t1.join(); t2.start(); for(int x = 0;x<80;x++) { //System.out.println("main....."+x); } System.out.println("over"); } }在日常线程编写时的几种方式:
代码示例:
class ThreadTest { public static void main(String[] args) { new Thread() { public void run() { for(int x = 0;x<100;x++) { System.out.println(Thread.currentThread().getName()+"..."+x); } } }.start(); Runnable r = new Runnable () { public void run() { for(int x = 0;x<100;x++) { System.out.println(Thread.currentThread().getName()+"..."+x); } } }; new Thread(r).start(); for(int x = 0;x<100;x++) { System.out.println(Thread.currentThread().getName()+"..."+x); } } }
总结
线程之间的通信,是日常生活当中经常会遇见的,而在多个线程之间对抢夺cpu执行权的时候要格外小心,用的不好可能导致程序无法继续运行,或者是使数据错乱。在学习这方面的知识时,还需要更多的去回顾并对知识点深度的理解。
相关文章推荐
- 【剑指offer】 面试题5: 从尾到头打印链表
- Android面试宝典
- 程序员的量化交易之路(30)--Cointrader之ConfigUtil(17)
- 程序员面试宝典 zigzag数组问题 p92 new二维数组
- 史上最全的iOS面试题及答案
- JAVA多线程和并发基础面试问答
- 黑马程序员---IOS基础---指针的概念及用法
- 阿里巴巴面试题目
- 微软面试题目
- 黑马程序员 - 技术 blog12 之 类库 by 八戒
- 黑马程序员+对Java中面向对象的总结
- 态度_面试从你进门就开始
- 【剑指offer】 面试题4: 替换空格
- 【免费公开课】如何肉身翻墙到硅谷?硅谷顶尖IT工程师在线传授面试技巧!
- Java面试之斐波纳契数列递归与非递归实现
- 码农们不得不重视的问题
- 国外程序员爱用苹果Mac电脑的10大理由
- 【剑指offer】 面试题3: 二维数组中的查找
- 黑马程序员---单例设计模式
- android面试题 不仅仅是面试是一个很好的学习