线程间通信

来源:互联网 发布:包工头app软件下载 编辑:程序博客网 时间:2024/06/03 16:34

之前的售票程序或者银行取钱例子中,所有线程执行的代码都是一样的。
线程间通信:A线程往里存,B线程往外取。
AB线程的动作不一致,也就需要有两个run方法。两个run方法就要存在两个线程当中。什么时候可以取,什么时候可以存,这就需要线程之间的通信。

例子:一个写入线程负责写,读取线程负责读,两者操作的是同一个资源,但是操作的动作是不同的。这时就要进行线程间通信。

package fighting;public class ThreadCommunication {/** * 线程间通讯:解决安全问题(要用同一个锁) * 其实就是多个线程在操作同一个资源,但是操作的动作不同。这样就需要通讯。 *  * 同步以后也会发生一种情况: * 就是输入线程往里存了一个,输出线程可能会执行了多次,取了多次, * 这样输出线程就会取到多次相同的值。这需要等待唤醒机制来解决。 * 等待唤醒机制:就是需要一个资源的flag。 *  * wait()和notify(),notifyAll() * 这几个方法都是用在同步中,要对持有监视器(也就是锁)的线程操作。 *  * 为什么线程用到的方法wait()和notify(),notifyAll()会定义在Oject类中,而不是定义在Thread类中? * 因为这些方法在操作同步的线程时,都必须要标识它们所操作线程持有的锁。 * 只有同一个锁上的被等待线程,可以被同一个锁上nofify唤醒。 * 不可以对不同锁中的线程唤醒。 * 也就是说,等待和唤醒必须是同一个锁。 * 而锁可以使任意对象,所以可以被任意对象调用的方法要定义在Oject类中。 * */public static void main(String[] args) {Resource r = new Resource();InputThread in = new InputThread(r);OutputThread out  = new OutputThread(r);new Thread(in).start();new Thread(out).start();}}//线程要操作的资源class Resource{String name;String sex;boolean flag = false;//等待唤醒机制的标志位}//输入的线程class InputThread implements Runnable{private Resource r;InputThread(Resource r){this.r=r;}public void run() {//同步synchronize写在run方法,就变成了单线程了,一个线程进来,其他线程就进不来了boolean b=true;int i=0;while(true){/** * 这里同步锁用了r,是因为InputThread和OutputThread两个线程操作的都是一个r。 * 这里也可以用InputThread.class或OutputThread.class或Resource.class, * 只要在内不能中是唯一的就可以,但是这样显得牵强。 */synchronized (r) {if(r.flag){//flag为True,说明已经有资源,不用再存,线程等待try {r.wait();//知识点:等待的线程存在内存线程池中,notify唤醒的都是线程池中的线程。         //注意:wait和notify方法必须标明是哪个锁,而且这个锁要和同步的锁是一个才可以。} catch (InterruptedException e) {e.printStackTrace();}}if(b){r.name="张三"+(i++);r.sex="男";b=false;}else{r.name="李四"+(i++);r.sex="女";b=true;}r.flag=true;r.notify();//唤醒等待的一个线程。还有一个方法notifyAll,唤醒所有等待的线程}}}}//取走的线程class OutputThread implements Runnable{private Resource r;OutputThread(Resource r){this.r=r;}public void run() {while(true){synchronized (r) {if(!r.flag){//flag为false,说明没有资源,不能取,线程等待try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(r.name+"..."+r.sex);r.flag=false;r.notify();}}}}

代码优化:把资源类Resource的成员私有化。

package fighting;public class ThreadCommunication {/**     * 代码优化:把资源私有化 */public static void main(String[] args) {Resource r = new Resource();InputThread in = new InputThread(r);OutputThread out  = new OutputThread(r);new Thread(in).start();new Thread(out).start();}}//线程要操作的资源class Resource{private String name;private String sex;private boolean flag = false;//等待唤醒机制的标志位public synchronized void set(String name,String sex){if(flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}this.name=name;//--->如果线程在执行完上一句,在这里被其他线程抢走了执行权,则这里会出现安全问题,所以这个方法需要同步this.sex=sex;flag=true;this.notify();//有资源了,将其他线程唤醒,通知可以取资源了}public synchronized void out(){if(!flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(name+"..."+sex);flag=false;this.notify();//这句唤醒要写在资源里面,如果线程里面,              //就会报错IllegalMonitorStateException(意思是当前线程不是此对象监视器的所有者)。}}//输入的线程class InputThread implements Runnable{private Resource r;InputThread(Resource r){this.r=r;}public void run() {//同步synchronize写在run方法,就变成了单线程了,一个线程进来,其他线程就进不来了boolean b=true;int i=0;while(true){if(b){r.set("张三"+(i++), "男");b=false;}else{r.set("李四"+(i++), "女");b=true;}//r.notify();//这句唤醒如果这里,就会报错IllegalMonitorStateException(意思是当前线程不是此对象监视器的所有者),           //因为同步是在资源类的方法上实现的,程序执行到这的时候,已经从同步方法里出来了。           //当前线程是InputThread这个线程,同步锁的所有者是Resource,}}}//取走的线程class OutputThread implements Runnable{private Resource r;OutputThread(Resource r){this.r=r;}public void run() {while(true){r.out();//r.notify();}}}



 

原创粉丝点击