5.1.5 线程间通信
wait:
notify();
notifyAll();
都使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
练习一:线程间通信
class Res { String name; String sex; boolean flag = false; } class Input implements Runnable { private Res r ; Input(Resr) { this.r= r; } public void run() { int x = 0; while(true) { synchronized(r) { if(r.flag) try{r.wait();}catch(Exceptione){} 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(Resr) { this.r= r; } public void run() { while(true) { synchronized(r) { if(!r.flag) try{r.wait();}catch(Exceptione){} 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(); } }
练习二:练习一的优化后
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(Exceptione){}//this.wait()中说明这里的锁是this this.name= name; this.sex= sex; flag= true; this.notify(); } public synchronized void out() { if(!flag) try{this.wait();}catch(Exceptione){} //this.wait()中说明这里的锁是this System.out.println(name+"........"+sex); flag= false; this.notify(); } } class Input implements Runnable { private Res r ; Input(Resr) { this.r= r; } public void run() { intx = 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(Resr) { this.r= r; } public void run() { while(true) { r.out(); } } } class InputOutputDemo2 { public static void main(String[] args) { Resr = new Res(); newThread(new Input(r)).start(); newThread(new Output(r)).start(); //上面两句是下面六句话的简化写法 /* Inputin = new Input(r); Outputout = new Output(r); Threadt1 = new Thread(in); Threadt2 = new Thread(out); t1.start(); t2.start(); */ } }
练习二:多个生产者消费者
class ProducerConsumerDemo { public static void main(String[] args) { Resourcer = 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(); } } /* 对于多个生产者和消费者。 为什么要定义while判断标记。 原因:让被唤醒的线程再一次判断标记。 为什么定义notifyAll, 因为需要唤醒对方线程。 因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。 */ class Resource { private String name; private int count = 1; private boolean flag = false; // t1 t2 public synchronized void set(String name) { while(flag) try{this.wait();}catch(Exceptione){}//t1(放弃资格) t2(获取资格) this.name= name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag= true; this.notifyAll(); } // t3 t4 public synchronized void out() { while(!flag) try{wait();}catch(Exceptione){}//t3(放弃资格) t4(放弃资格) System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name); flag= false; this.notifyAll(); } } class Producer implements Runnable { private Resource res; Producer(Resourceres) { this.res= res; } publicvoid run() { while(true) { res.set("+商品+"); } } } class Consumer implements Runnable { private Resource res; Consumer(Resourceres) { this.res= res; } public void run() { while(true) { res.out(); } } }
5.1.6 JDK1.5 中提供了多线程升级解决方案。Lock
将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。
该对象可以Lock锁进行获取。
该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock
unlock
newCondition()
Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
练习四:用JDK1.5 新特性 Lock来练习多个生产者和消费者
import java.util.concurrent.locks.*; 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(); } } class Resource { private String name; private int count = 1; private boolean flag = false; // t1 t2 private Lock lock = new ReentrantLock(); private Condition condition_pro = lock.newCondition(); private Condition condition_con = lock.newCondition(); public void set(String name)throwsInterruptedException { lock.lock(); try { while(flag) condition_pro.await();//t1,t2 this.name= name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag= true; condition_con.signal();//唤醒condition_con } finally { lock.unlock();//释放锁的动作一定要执行。 } } // t3 t4 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();//唤醒condition_pro } finally { lock.unlock(); } } } class Producer implements Runnable { private Resource res; Producer(Resourceres) { this.res= res; } public void run() { while(true) { try { res.set("+商品+"); } catch(InterruptedException e) { } } } } class Consumer implements Runnable { privateResource res; Consumer(Resourceres) { this.res= res; } public void run() { while(true) { try { res.out(); } catch(InterruptedException e) { } } } }
5.1.7停止线程
定义循环结束标记
因为线程运行代码一般都是循环,只要控制了循环即可。
使用interrupt(中断)方法。
该方法是结束线程的冻结状态,使线程回到运行状态中来。
注:stop方法已经过时不再使用。
stop方法已经过时。
如何停止线程?
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。
就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法 interrupt();
练习五:如何停止线程
class StopThread implements Runnable { private boolean flag =true; public void run() { while(flag) { System.out.println(Thread.currentThread().getName()+"....run"); } } public void changeFlag() { flag= false; } } class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.setDaemon(true);//守护线程_属于后台线程 t2.setDaemon(true);//守护线程 t1.start(); t2.start(); int num = 0; while(true) { if(num++== 60) { //st.changeFlag(); //t1.interrupt(); //t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"......."+num); } System.out.println("over"); } }
练习六:用匿名内部类创建两种线程
class ThreadTest { public static void main(String[] args) { new Thread() { public void run() { for(intx=0; x<100; x++) { System.out.println(Thread.currentThread().getName()+"....."+x); } } }.start(); for(intx=0; x<100; x++) { System.out.println(Thread.currentThread().getName()+"....."+x); } Runnable r = new Runnable() { public void run() { for(intx=0; x<100; x++) { System.out.println(Thread.currentThread().getName()+"....."+x); } } }; new Thread(r).start(); } }
5.1.8 Thread类中一些常用的方法
setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
join()
等待该线程终止。
yield()
暂停当前正在执行的线程对象,并执行其他线程。
setPriority(int newPriority)
更改线程的优先级。
MAX_PRIORITY
线程可以具有的最高优先级。
MIN_PRIORITY
线程可以具有的最低优先级。
NORM_PRIORITY
分配给线程的默认优先级。