多线程通信引入:
class Resource { String name; String sex; boolean flag=true; } class Input implements Runnable { private Resource r; Input(Resource r) { this.r=r; } public void run() { int x=0; while(true) { synchronized(r)//也可以用类文件对象Input.class/Output.class { if(r.flag) { if(x==0) { r.name="Mike"; r.sex="men"; r.flag=false; } else { r.name="Lili"; r.sex="女女女女女女"; r.flag=false; } x=(x+1)%2; } } } } } class Output implements Runnable { private Resource r; Output(Resource r) { this.r=r; } public void run() { while(true) { synchronized(r) { if(!r.flag) { try{Thread.sleep(100);}catch(Exception e){} System.out.println(r.name+"----"+r.sex+"----"+r.flag); r.flag=true; } } } } } class InOutDemo { public static void main(String[] args) { Resource r=new Resource(); new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); } } /* 对输出结果分析: 可能出现: 一个线程在执行完: r.name="Mike"; r.sex="men"; 下次执行else内容时 r.name="Lili"; 执行到此,cpu切换到另一线程: 输出r.name="Lili",r.sex="men"; */ /* 解决以上问题采用同步代码块: 对输出结果分析: 为什么没有输出Mike--men,Lili--"女女女女"交替? 这是因为,当一个线程执行完赋值动作后, Cpu可能切换到另一个线程进行循环打印 */
多线程通信-等待唤醒机制
/* 等待唤醒机制 比喻:(有助于理解,在毕老师基础上加了点,可能比喻有偏差,仅供参考) 一种"冰棍化了"了游戏,一群小朋友来到一个院子(锁),一个主角(CPU)追一群小朋友(线程),当 其中一人被追上(执行)时,他喊"冰棍"(wait),主角不能在抓这个人了. 而其他人在躲闪过程中可以救(notify)这个喊了冰棍的人. (API:这翻译真心拗口,英语不好吃亏!) wait(): 前提:当前线程必须拥有此 对象监视器(锁)。 该线程发布(放弃)对此监视器(锁)的所有权并等待,直到其他线程通过调用 notify 方法, 或 notifyAll 方法通知 在此对象的监视器上等待 的线程醒来. 然后该线程将等到 重新获得对监视器的所有权后 才能继续执行。 */ //需求:为了达到赋值一次,输出一次. class Resource { String name; String sex; boolean flag=false; } class Input implements Runnable { private Resource r; Input(Resource r) { this.r=r; } public void run() { int x=0; while(true) { synchronized(r)//也可以用类文件对象Input.class/Output.class { if(r.flag) try{r.wait();}catch(Exception e){}//当已赋值完成,该线程等待 //wait()方法是Object中的方法,该方法会抛出异常,不能在run上抛出 if(x==0) { r.name="Mike"; r.sex="men"; } else { r.name="Lili"; r.sex="女女女女女女"; } r.flag=true; r.notify();//唤醒另一个线程去取 x=(x+1)%2; } } } } class Output implements Runnable { private Resource r; Output(Resource 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); r.flag=false; r.notify(); } } } } class InOutDemo2 { public static void main(String[] args) { Resource r=new Resource(); new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); } } /* 注意: 关于r.wait():必须标识出wait()操作的 线程 所持有的锁(r) 因为可能有同步嵌套(不同的锁) r.notify()唤醒的是: r锁上的 等待线程 wait(),notify(),notifyAll() 都使用在同步中,因为要对持有监视器(锁)的线程操作 所以要使用在同步中,因为只有同步才具有锁 Q3.为什么wait()方法定义在Object类中? 因为 锁可以是任意对象,任意对象都能调用的方法->定义在Object中 同一个锁上等待的线程,只能被同一个锁上线程notify 不可以对不同锁中的线程进行唤醒. */
对以上代码简单优化(同步函数)
class Resource { private String name,sex; private boolean flag=false; public synchronized void setValue(String name,String sex) { if(flag) try{this.wait();}catch(Exception e){}//this可以省略 this.name=name; this.sex=sex; flag=true; this.notify(); } public synchronized void printValue() { if(!flag) try{this.wait();}catch(Exception e){} System.out.println(this.name+"----"+this.sex); flag=false; this.notify(); } } class Input implements Runnable { private Resource r; Input(Resource r) { this.r=r; } public void run() { int x=0; while (true) { if(x==0) r.setValue("mike","man"); else r.setValue("Lili","女女女"); x=(x+1)%2; } } } class Output implements Runnable { private Resource r; Output(Resource r) { this.r=r; } public void run() { int x=0; while (true) { try{Thread.sleep(100);}catch(Exception e){} r.printValue(); } } } class InOutDemo3 { public static void main(String[] args) { Resource r=new Resource(); new Thread(new Input(r)).start(); new Thread(new Input(r)).start(); } }
生产者-消费者(两个线程生产,两个线程消费)
class Resource { private int count=1; private boolean flag=false; private String name; //Thread-0 Thread-1 public synchronized void setValue(String name) { //if(flag) while(flag) try{this.wait();}catch(Exception e){}//① this.name=name+"---"+count++; System.out.println(Thread.currentThread().getName()+"...---生产者生产----..."+this.name); flag=true; //this.notify();//② this.notifyAll(); } //Thread-2 Thread-3 public synchronized void printValue() { //if(!flag) while(!flag) try{this.wait();}catch(Exception e){}//③ System.out.println(Thread.currentThread().getName()+"---消费者消费---"+name); flag=false; //this.notify();//④ this.notifyAll(); } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r=r; } public void run() { int x=0; while (true) r.setValue("+商品+"); } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r=r; } public void run() { int x=0; while (true) r.printValue(); } } class ProCon { public static void main(String[] args) { Resource r=new Resource(); /* //这样写也是可以的,虽然是两个生产者/消费者 对象,但 //都是执行的r对象中的方法. new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); */ Producer p=new Producer(r); new Thread(p).start(); new Thread(p).start(); Consumer c=new Consumer(r); new Thread(c).start(); new Thread(c).start(); } } /* 打印结果:可能出现两次生产,只消费一次.生产一次,消费两次... 分析:(其中一种可能:生产两次,消费一次) 0 1.cpu切换到0线程->执行生产->Thread-0 生产者生产 商品 1->再次执行-> 0线程等待①位置. 0 1 2.cpu切换到1线程->1线程等待①位置. 1 3.cpu切换到2线程->执行消费->Thread-2 消费者消费 商品 1->④notify 0-> 1 2 ->再次执行->2线程等待③位置. 1 2 3 4.cpu切换到3线程->3线程等待③位置 2 3 5.cpu切换到0线程->从①开始执行->Thread-0 生产者生产 商品 2->②notify 1-> 2 3 0 ->再次执行->0线程等待①位置 3 0 6.cpu切换到1线程->从①开始执行->Thread-1 生产者生产 商品 3->②notify 2 3 0 1 ->再次执行->1线程等待①位置 0 1 7.cpu切换到2线程->从③开始执行->Thread-2 消费者消费 商品 3->④notify 3 0 1 2 ->再次执行->2线程等待③位置 ...... 另一种可能:先生产即可. 以上关键在5,6 0线程唤醒1线程,而1线程没有执行flag判断,继续向下执行. ->如果把if改成while(flag)/while(!flag) ->在第五步0唤醒1->1去等待,0也去等待->全部等待 那么使用notifyAll()全部唤醒 实际上 本方依然等待,而让对方执行. */
生产者-消费者JDK5.0升级(Lock,Condition)
/*
JDK1.5版本中提供了多线程的升级解决方案
(显式的锁机制)
将同步Synchronized替换成现实的Lock操作
将Object中的wait,notify,notifyall,替换成了Condition对象.
该对象可以通过Lock锁进行获取(newCondition())
在该示例中,实现了本方唤醒对方的操作*/
import java.util.concurrent.locks.*; class Resource { private int count=1; private boolean flag=false; private String name; private Lock lock=new ReentrantLock();//锁对象,ReentrantLock类实现Lock private Condition condition_pro=lock.newCondition(); private Condition condition_cus=lock.newCondition(); //Thread-0 Thread-1 public void setValue(String name)throws InterruptedException { lock.lock(); System.out.println(Thread.currentThread().getName()+"①---"+flag); try { if(flag) condition_pro.await();//与此 Condition 相关的锁以原子方式释放 System.out.println(Thread.currentThread().getName()+"②---"+flag); this.name=name+"---"+count++; System.out.println(Thread.currentThread().getName()+"...---生产者生产----..."+this.name); flag=true; //condition.signal(); //condition.singalAll(); condition_cus.signal(); System.out.println(Thread.currentThread().getName()+"③---"+flag); } finally { System.out.println("---unlock---"); lock.unlock();//必须finally,有可能await抛出异常不释放锁 } } //Thread-2 Thread-3 public synchronized void printValue()throws InterruptedException { lock.lock(); System.out.println(Thread.currentThread().getName()+"④---"+flag); try { if(!flag) condition_cus.await(); System.out.println(Thread.currentThread().getName()+"⑤---"+flag); System.out.println(Thread.currentThread().getName()+"---消费者消费---"+name); flag=false; //condition.signal();全等待 //condition.signalAll(); condition_pro.signal();//唤醒condition_pro对象上的await(其中一个线程) System.out.println(Thread.currentThread().getName()+"⑥---"+flag); } finally { System.out.println("---UNLOCK---"); lock.unlock(); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r=r; } public void run() { int x=0; while (true) try { r.setValue("+商品+"); } catch (InterruptedException e) { } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r=r; } public void run() { int x=0; while (true) try { r.printValue(); } catch (InterruptedException e) { } } } class ProCon2 { public static void main(String[] args) { Resource r=new Resource(); Producer p=new Producer(r); new Thread(p).start(); new Thread(p).start(); Consumer c=new Consumer(r); new Thread(c).start(); new Thread(c).start(); } } /* //Lock 替代了 synchronized 方法和语句的使用, //Condition 替代了 Object 监视器方法的使用 //wait,notify在同步中需要标识所属的锁 //因此通过Lock方法获取condition的实例 //也就是说Condition 实例实质上被绑定到一个锁上 *//* 以上仅仅替换了原先的代码(换汤不换药) 依然存在把本方线程唤醒的可能. 因此再使用一个condition对象 可以有多个实例绑到一个锁上,由此体现出新版本优点 同步中只能有一个对象绑定到一个锁. */ /* 虽然唤醒的是对方线程,但是依然需要while判断标记: 否则依然出现生产两次,而消费一次: cpu切换到0->0生产->在执行->0等待 cpu切换到2->2消费->唤醒0->在执行->2等待 cpu切换到1->1生产 cpu切换到0->0生产 ... */
interrupt方法:
/* stop 方法已经过时. 如何停止线程? 只有一种,run方法结束. 开启多线程运行,运行代码通常是循环结构 只要控制住循环,就可以让run方法结束,也就是线程结束. Thread类中的interrupt方法:(不是终止线程) 当没有指定方式让冻结的线程恢复到运行状态时, 这时需要对冻结(阻塞)进行清除.强制让线程恢复到就绪状态中来. 这样就可以操作标记让线程结束. 清除的根本:使wait方法抛出InterruptedException */ 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.start(); t2.start(); int x=0; while(true) { if(x++==60) { //s.changeFlag(); t1.interrupt(); t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"...."+x); } System.out.println(Thread.currentThread().getName()+"....over"); } }
守护(后台)线程:
/* 守护线程: 当线程被标记为守护线程(后台线程) 开启,执行与一般线程均无区别, 但当所有前台线程(未标记)都执行完->守护线程会自动结束 该方法必须在启动线程前调用。 */ class StopThread implements Runnable { private boolean flag=true; public synchronized void run() { while(flag) { System.out.println(Thread.currentThread().getName()+"....run..."); } } public void changeFlag() { flag=false; } } class setDaemonDemo { 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);//on为true 该线程被标记为守护线程 t1.start(); t2.start(); int x=0; while(true) { if(x++==60) { break; } System.out.println(Thread.currentThread().getName()+"...."+x); } System.out.println(Thread.currentThread().getName()+"....over"); } } /* 主线程一执行完,t1,t2自动结束. */
为什么在主线程”over”后,守护线程又执行了一会?其实并非这样,之所以在over后又打印了守护线程的输出语句这是因为:可能先执行后被打印
join方法:
/* join 方法: 当A线程执行到了B线程的.join方法时,A线程就会等待, 等B线程都执行完,A才会执行 join可以用来临时加入线程. */ class Demo implements Runnable { public void run() { for(int i=0;i<70;++i) { System.out.println(Thread.currentThread().getName()+"....."+i); } } } class JoinDemo { public static void main(String[] args) throws Exception { Demo d=new Demo(); Thread t1=new Thread(d); Thread t2=new Thread(d); t1.start(); t2.start(); t1.join();//可以用于临时加入一个线程,让该线程执行完. //当主线程执行到此,把cpu执行权交给t1,主线程会等t1的run()结束后,主线程才能继续执行 //而t2是否执行结束不影响主线程 for(int i=0;i<80;++i) { System.out.println(Thread.currentThread().getName()+"....."+i); } } }
yield方法:
class Demo implements Runnable { public void run() { for(int i=0;i<70;++i) { System.out.println(Thread.currentThread().getName()+"....."+i); Thread.yield();//稍微减缓线程运行,0执行下,1执行下,0执行下... } } } class YieldDemo { public static void main(String[] args) throws Exception { Demo d=new Demo(); Thread t1=new Thread(d); Thread t2=new Thread(d); t1.start(); t2.start(); //t1.setPriority(Thread.MAX_PRIORITY);//使0线程具有更高优先级(1-10,默认为5) for(int i=0;i<80;++i) //cpu执行该线程频率高点 { System.out.println(Thread.currentThread().getName()+"....."+i); } } }