【多线程】线程间的通信

举一个例子来看一下线程间的通信。

示例:

子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程又循环100次。如此循环50次,请写出程序。

思路:

先找两个方法,分别执行子线程循环10次,主线程循环100次。并这两个同步。由外层控制,循环50次,并通过线程间通信得到,当子线程执行结束后,由主线程执行。并且主线程执行结束后,子线程也能开始执行。

实现:

先写了一个类,用于子主线程分别循环10次和100次。

class Business{
    private boolean bShouldSub=true;
    public synchronized void sub(int j){
            for (int i = 0; i < 10; i++) {
            System.out.println("sub thread sequece of: "+i);
            }
    }

    public synchronized void main(int j){
        for (int i = 0; i < 100; i++) {
            System.out.println("main thread sequece of: "+i );
    }
}

代码分析:
将两个方法都用synchronized 锁控制。都对this即同一个类的实例加锁,能保证两个方法同步。当子线程执行过程,主线程不会执行。
此时有外层再去控制循环50次。

public static void main(String[] args) {

    final Business business =new  Business();
    new Thread(
            new Runnable(){
                @Override 
                public void run(){
                    for (int i = 0; i< 50; i++) {
                        business.sub(i);
                            }

                }
            }

            ).start();

    for (int j = 0; j < 50; j++) {
        business.main(j);    
    } 
  }
}

代码分析:
主方法能保证代码之间进行50次循环。但并不能保证是子线程和主线程交替执行。这时候就需要我们说的线程通信了。通过标志来标识当前是主还是子在执行,如果主执行,则让子等待。当主执行完之后通知子线程执行。主线程同理。
即修改业务逻辑代码。

class Business{
    private boolean bShouldSub=true;
    public synchronized void sub(int j){
        //通过标识判断,如果当前不是子线程执行
        while (!bShouldSub) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for (int i = 0; i < 10; i++) {
            System.out.println("sub thread sequece of: "+i);
            }
        bShouldSub=false;
        this.notify();//唤醒正在等待的线程
    }

    public synchronized void main(int j){
        while (bShouldSub) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        for (int i = 0; i < 100; i++) {
            System.out.println("main thread sequece of: "+i );
    }
        bShouldSub=true;
        this.notify();      
    }
}

代码分析:
通过this.wait 和 this.notify相互通信。当子线程判断当前是主线程执行时,则子线程进入等待。等主线程执行完之后,会唤醒该子线程继续执行。

此外补充说明:
1.将用到共同数据(包括同步锁)或共同算法的若干个方法应该归在同一个类身上,这种设计正好体现了高内聚和程序的健壮性。
2.标志位判断时,用while不用if 。是防止程序被假唤醒。这样while还会再次确定一下“真的该我了么”。对程序多了一层保护。

线程间通信:


一)wait,notify,notifyAll 形式

wait,notify,(还有唤醒所有线程的notifyAll ) 都是object类提供的方法,不属于thread类。所以这三个方法都必须有同步监视器对象来调用。
①对于使用synchronized修饰的同步方法,因为该类的默认实例(this)就是同步监视器,所以可以再同步方法中直接调用这三个方法。
②对于使用synchronized修饰的同步代码块,同步监视器是synchronized后括号里的对象,所以必须使用该对象调用这三个方法。

 synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
     }

wait():当前线程等待并释放对该同步监视器的锁定;可设定时间;需要由notify或notifyAll唤醒。

notify():随机唤醒此同步监视器上等待的一个线程。
notifyAll():唤醒此同步监视器上等待的所有线程。
该方式的弊端是,如果不使用synchronized同步,改用lock对象,则不存在隐式同步监视器,也就不能用该方式。

二)使用condition控制线程通信

该方式适用于用Lock锁时。通过调用Lock对象的newCondition()方法得到特定Lock实例的Condition实例。
方法和方式一基本对应。分别是:await(),signal(),signalAll();

public class Thread{
    private final Lock lock=new ReentrantLock();
    private final Condition  connd=new lock.newCondition(); 
    .....
    pbulic void mathod1()
    {
        lock.lock();
        if(flag)
        {
            cond.await();
        }
        else
        {
        ...
        cond.singalAll();
        }
    }
}

三)阻塞队列(BlockingQueue)控制线程通信

BlockingQueue特性:当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程被阻塞;当消费者线程试图从BlockingQueue中取元素,如果队列已空,则被阻塞。

总结:

本文介绍了三种线程间的通信方式。方式一适用于synchronized修饰时,采用wait,notify,notifyAll方式。方式二适用于用Lock修饰时,采用condition。方式三个人认为比较适用于生产者和消费者问题。欢迎补充,指正。

猜你喜欢

转载自blog.csdn.net/u010176014/article/details/52231786