Skip to content

线程协作

当多个线程一起工作解决某个问题的时候,如果某些线程必须在其他线程完成之后裁可以继续,那么久需要对线程进行协调。通常由以下几种方式

方式一:调用线程 join()方法

在一个线程调用另外一个线程的 join()方法, 会将当前线程挂起,直到目标线程结束

java
public class JoinSychronization {

    private class AThread extends Thread {

        private String name;

        public AThread(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            System.out.println("执行A线程 " + name);
        }
    }


    private class BThread extends Thread {

        private AThread aThread;
        private String name;

        public BThread(AThread aThread,String name) {
            this.aThread = aThread;
            this.name = name;
        }

        @Override
        public void run() {
            try {
                aThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("执行B线程方法 " + name);
        }
    }

    public void test() {
        AThread thread = new AThread("demoA");
        BThread demoB = new BThread(thread, "demoB");
        demoB.start();
        thread.start();
    }


    public static void main(String[] args) {
        JoinSychronization sychronization = new JoinSychronization();
        sychronization.test();
    }
}

备注:

  • test 方法中,虽然说 demoB 线程先启动, 但是 demoB 线程调用了 thread 线程的 join()方法,所以 demoB 线程会等待 thread 线程结束裁继续执行, 所以先输出 执行 A 线程 demoA, 之后输出 执行 B 线程方法 demoB

运行结果如下:

text
执行A线程 demoA
执行B线程方法 demoB

方式二:通过 Object wait()/notify()/notifyAll()协调

调用 wait() 使线程等待某个条件满足,线程在等待的时候会被挂起,当其他线程线程的运行使这个条件满足时,其他线程会调用 notify() 或者 notifyAll() 唤醒挂起的线程, 需要注意的是,只能在同步方法或者同步控制块才能使用,否则会抛出 IllegalMonitorStateException, wait()挂起,线程会释放锁,否则其他线程就无法进入对象的同步方法或者同步控制块,那么久无法执行 notify() 或者 notifyAll() 来唤醒挂起的线程, 会造成死锁

notify() 跟 notifyAll()的区别:

  • 唤醒数量不同

    notify()方法只会随机唤醒等待队列中的一个线程,而 notifyAll()方法则会唤醒等待队列中的所有线程。

  • 竞争情况不同

notify()方法只会唤醒等待队列中的一个线程,并使其与其他线程竞争获取锁,这可能会导致某些线程无法被唤醒或者一直处于等待状态。 notifyAll()方法则会唤醒等待队列中的所有线程,并使它们竞争获取锁,这样可以使所有线程都有机会获取锁并进入运行状态,从而避免了一些线程一直处于等待状态。

java
public class WaitNotifySychonization {

    public synchronized void begin() {
        System.out.println("开始执行");
        notifyAll();
    }

    public synchronized  void end() {
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("结束执行");
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        WaitNotifySychonization waitNotifySychonization = new WaitNotifySychonization();
        executorService.execute(waitNotifySychonization::end);
        executorService.execute(waitNotifySychonization::begin);
        Thread.sleep(2000);
        executorService.shutdown();
    }
}

运行结果

text
开始执行
结束执行

方式三:await()/signal()/signalAll()

可以通过 Condition 类来实现线程之间的协调,可以在 condition 上调用 await()方法使线程进行等待,其他线程调用 signal()或 signalAll()方法唤醒等待的线程 await() 可以指定等待条件, 比 wait()更加灵活 使用 Lock 来获取 Condition 对象

signalAll 通知所有使用了同一个 Condition 对象的线程。signal()通知所有使用了 Condition 对象的某一个线程,通过源码可以看到通知的线程是位于队首的那个。

java
public class ConditionSychonization {

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    public void beore() {
        lock.lock();
        try {
            System.out.println("before");
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }

    public void after() {
        lock.lock();
        try {
            condition.await();
            System.out.println("after");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        ConditionSychonization sychonization = new ConditionSychonization();
        executorService.execute(sychonization::after);
        executorService.execute(sychonization::beore);
        Thread.sleep(2000);
        executorService.shutdown();
    }
}

运行结果

text
before
after

Released under the MIT License.