黑马程序员——JAVA基础之多线程的线程间通讯等
------- android培训、java培训、期待与您交流! ----------
线程间通讯:
其实就是多个线程在操作同一个资源,但是动作不同。
wait();
在其他线程调用此对象的notify()方法或notifyAll()方法,或者超过指定的时间量前,导致当前线程等待。
当前线程必须拥有此对象监视器。
notify();
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。
选择是任意性的,并在对实现做出决定时发生。
线程通过调用其中一个
wait
方法,在对象的监视器上等待。
notifyAll();
唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个
wait
方法,在对象的监视器上等待。
思考1:wait ( ),notify ( ),notifyAll ( ),用来操作线程为什么定义在了Object 类中?
1,这些方法存在与同步中。
2,使用这些方法时必须要标识所属的同步的锁。
3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
思考2:wait ( ) ,sleep ( ) 有什么区别?
wait ( ) : 释放cpu执行权,释放锁。
sleep ( ) : 释放cpu执行权,不释放锁。
/** * * 多线程通讯示例: * 多线程通讯就是多个线程共同操作同一个资源,但是动作不同 * 例:开启两个线程,交替输入输出两个人的名字和年龄 * */ public class ThreadTest { public static void main(String[] args) { Person p = new Person(); Input in = new Input(p); Output out = new Output(p); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); } } //声明一个Person类,有两个属性,姓名和性别 class Person { String name; String sex; } //声明一个输入线程继承Runnable。传入名字和性别 class Input implements Runnable { private Person p; Input (Person p) { this.p = p; } public void run() { int x = 0; while(true) { synchronized (p) { if (x==0) { p.name = "lilei"; p.sex = "boy"; } else { p.name = "hanmeimei"; p.sex = "girl"; } x = (1+x)%2; } } } } //声明一个输出线程的类继承Runnable,输出姓名和性别 class Output implements Runnable { private Person p; Output(Person p) { this.p = p; } public void run() { while(true) { synchronized (p) { System.out.println("name: "+p.name+"-------sex: "+p.sex); } } } }
等待唤醒机制:
改进一下这个程序,让输入输出交替进行
/** * * 多线程通讯示例: * 多线程通讯就是多个线程共同操作同一个资源,但是动作不同 * 例:开启两个线程,交替输入输出两个人的名字和年龄 * */ public class ThreadTest { public static void main(String[] args) { Person p = new Person(); Input in = new Input(p); Output out = new Output(p); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); } } //声明一个Person类,有两个属性,姓名和性别 class Person { String name; String sex; boolean flat; } //声明一个输入线程继承Runnable。传入名字和性别 class Input implements Runnable { private Person p; Input (Person p) { this.p = p; } public void run() { int x = 0; while(true) { synchronized (p) { if (p.flat) try{p.wait();}catch(Exception e){} if (x==0) { p.name = "lilei"; p.sex = "boy"; } else { p.name = "hanmeimei"; p.sex = "girl"; } x = (1+x)%2; p.flat = true; p.notify(); } } } } //声明一个输出线程的类继承Runnable,输出姓名和性别 class Output implements Runnable { private Person p; Output(Person p) { this.p = p; } public void run() { while(true) { synchronized (p) { if (!p.flat) try{p.wait();}catch(Exception e){} System.out.println("name: "+p.name+"-------sex: "+p.sex); p.flat = false; p.notify(); } } } }
发现有些数据不能被外界直接调用,具有安全隐患,所以改进如下:
/** * * 多线程通讯示例: * 多线程通讯就是多个线程共同操作同一个资源,但是动作不同 * 例:开启两个线程,交替输入输出两个人的名字和年龄 * */ public class ThreadTest { public static void main(String[] args) { Person p = new Person(); Input in = new Input(p); Output out = new Output(p); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); } } //声明一个Person类,有两个属性,姓名和性别 class Person { //私有数据,更安全 private String name; private String sex; private boolean flat; public synchronized void set(String name,String sex) { if (flat) try{this.wait();}catch(Exception e){} this.name = name; this.sex = sex; flat = true; this.notify(); } public synchronized void out() { if (!flat) try{this.wait();}catch(Exception e){} System.out.println("name: "+name+"-------sex: "+sex); flat = false; this.notify(); } } //声明一个输入线程继承Runnable。传入名字和性别 class Input implements Runnable { private Person p; Input (Person p) { this.p = p; } public void run() { int x = 0; while(true) { if (x==0) p.set("lilei","boy"); else p.set("hanmeimei","girl"); x = (x+1)%2; } } } //声明一个输出线程的类继承Runnable,输出姓名和性别 class Output implements Runnable { private Person p; Output(Person p) { this.p = p; } public void run() { while(true) { p.out(); } } }
为什么定义notifyAll:
因为需要唤醒对方线程。
因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。
/** * * 当出现两个以上多线程进行多线程通讯的时候,需要用的notifyAll(); * 例子: * 生产者消费者 * */ public class ProCusDemo { public static void main(String[] args) { Resource r = new Resource(); Pro p = new Pro(r); Cus c = new Cus(r); Thread t1 = new Thread(p); Thread t2 = new Thread(p); Thread t3 = new Thread(c); Thread t4 = new Thread(c); t1.start(); t2.start(); t3.start(); t4.start(); } } //声明一个资源类,赋予基本属性 class Resource { private String name; private int count = 1; private boolean flat; public synchronized void set(String name) { while (flat)//用while让被唤醒的线程再一次判断标记 try{wait();}catch(Exception e){} this.name = name+"-----"+count++; System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name); flat = true; notifyAll(); } public synchronized void out() { while (!flat)//用while让被唤醒的线程再一次判断标记 try{wait();}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"---消费者---"+this.name); flat = false; notifyAll(); } } class Pro implements Runnable { private Resource r; Pro(Resource r) { this.r = r; } public void run() { while (true) { r.set("商品"); } } } class Cus implements Runnable { private Resource r; Cus(Resource r) { this.r = r; } public void run() { while (true) { r.out(); } } }
JDK1.5 中提供了多线程升级解决方案。
将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。
该对象可以Lock锁进行获取。
该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock
unlock
newCondition()
Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
<span style="font-size:14px;">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; 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();//t1,t2 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) { } } } } </span>
如何停止线程:
1. 定义循环结束标记,因为线程运行代码一般都是循环,只要控制了循环即可。
2. 注:stop方法已经过时不再使用。
stop方法已经过时,如何停止线程?
只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。使用interrupt(中断)方法。
该方法是结束线程的冻结状态,使线程回到运行状态中来。
<span style="font-size:14px;">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 st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); 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"); } } </span>
守护线程:
前台线程执行完之后后台线程自动执行完,主线程是前台线程。
<span style="font-size:14px;">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 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"); } } </span>
join用法:
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
<span style="font-size:14px;">class Demo implements Runnable { public void run() { for(int x=0; x<70; x++) { System.out.println(Thread.currentThread().getName()+"...."+x); } } } 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(); t1.join();//此时t1线程获得执行权,主线程冻结。 t2.start(); for(int x=0; x<80; x++) { System.out.println("main....."+x); } System.out.println("over"); } } </span>
yield() :
可以稍微减缓程序运行,产生近似于交替运行的效果。
class Demo implements Runnable { public void run() { for(int x=0; x<70; x++) { System.out.println(Thread.currentThread().toString()+"....."+x);//toString()显示优先级,优先级1~10,默认5 Thread.yield();//让程序稍微暂停一下,释放执行权,产生交替运行的效果 } } } 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(); //t1.setPriority(Thread.MAX_PRIORITY); 设置最大优先级 t2.start(); for(int x=0; x<80; x++) { System.out.println("main....."+x); } System.out.println("over"); } }
------- android培训、java培训、期待与您交流! ----------