多线程顺序执行

多线程顺序执行

Scroll Down

多线程顺序执行

join方法

​ 主要的作用是同步,使线程之间的同时执行,争抢时间片变为串行执行,比如,在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。

​ 首先如果没有调用join

    public static void main(String[] args) throws InterruptedException {
    Thread t1  =  new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println("当前线程是"+Thread.currentThread().getName());
            }
        },"A");
    Thread t2  =  new Thread(()->{
        for (int i = 0; i < 10; i++) {
            System.out.println("当前线程是"+Thread.currentThread().getName());
        }
    },"B");
    Thread t3  =  new Thread(()->{
        for (int i = 0; i < 10; i++) {
            System.out.println("当前线程是"+Thread.currentThread().getName());
        }
    },"C");
        t1.start();
        t2.start();
        t3.start();
        System.out.println("主线程");
    }

执行顺序是不确定的,因为主线程、和A、B、C线程都有可能争抢到执行权。

加入join后

        t1.start();
        t1.join();//主线程进入阻塞状态,等t1线程执行完毕后主线程继续执行
        t2.start();
        t2.join();//主线程进入阻塞状态,等t2线程执行完毕后主线程继续执行
        t3.start();
        t3.join();//主线程进入阻塞状态,等t3线程执行完毕后主线程继续执行

image.png

补充:

1️⃣ 如果A线程中掉用B线程的join(10),则表示A线程会等待B线程执行10毫秒,10毫秒过后,A、B线程并行执行。需要注意的是,jdk规定,join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()

2️⃣ join()方法必须在线程start()方法调用之后调用才有意义

join方法的实现原理

image.pngimage.png

可知,join方法是通过调用线程的wait方法来达到同步的目的,比如上例中,t1调用join后,主线程被阻塞,当t1执行完毕后,会调用notifyall方法。

使用单一化线程池实现

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        Thread t1  =  new Thread(()->{
            for (int i = 0; i < 2; i++) {
                System.out.println("当前线程是A"+Thread.currentThread().getName());
            }
        },"A");
        Thread t2  =  new Thread(()->{
            for (int i = 0; i < 2; i++) {
                System.out.println("当前线程是B"+Thread.currentThread().getName());
            }
        },"B");
        Thread t3  =  new Thread(()->{
            for (int i = 0; i < 2; i++) {
                System.out.println("当前线程是C"+Thread.currentThread().getName());
            }
        },"C");
        threadPool.submit(t1);
        threadPool.submit(t2);
        threadPool.submit(t3);
        threadPool.shutdown();
    }

​ 可以看到,newSingleThreadExecutor()是通过 new LinkedBlockingQueue()实现的(基于链表无界阻塞队列(其默认容量为Interger.MAX),按照FIFO排序),所以能够按照顺序执行。

​ 实际在就绪状态的只有t1这个线程,t2,t3则会被添加到队列中,当t1执行完毕后,则会继续执行队列中的其他线程。

image.png

Condition方法

Condition(条件变量):通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。

  • Condition中**await()**方法类似于Object类中的wait()方法。
  • Condition中**await(long time,TimeUnit unit)**方法类似于Object类中的wait(long time)方法。
  • Condition中**signal()**方法类似于Object类中的notify()方法。
  • Condition中**signalAll()**方法类似于Object类中的notifyAll()方法。

CountDownLatch方法

CountDownLatch:位于java.util.concurrent包下,利用它可以实现类似计数器的功能。

应用场景:比如有一个任务C,它要等待其他任务A,B执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

package MultiThreaded.dxc_sequence;


import java.util.concurrent.CountDownLatch;


public class ThreadCountDownLatchDemo {

    /**
     * 用于判断线程一是否执行,倒计时设置为1,执行后减1
     */
    private static CountDownLatch c1 = new CountDownLatch(1);

    /**
     * 用于判断线程二是否执行,倒计时设置为1,执行后减1
     */
    private static CountDownLatch c2 = new CountDownLatch(1);

    public static void main(String[] args) throws InterruptedException {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程1");
                //对c1倒计时-1
                c1.countDown();
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //等待c1倒计时,计时为0则往下运行
                    c1.await();
                    System.out.println("线程2");
                    //对c2倒计时-1
                    c2.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //等待c2倒计时,计时为0则往下运行
                    c2.await();
                    System.out.println("线程3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });


        System.out.println("主线程-A");
        thread3.start();
        Thread.sleep(1001);
        System.out.println("主线程-B");
        thread2.start();
        System.out.println("主线程-C");
        thread1.start();

    }
}

CyclicBarrier(回环栅栏)

CyclicBarrier(回环栅栏):通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。调用一次await()barrier就加1.

应用场景:公司组织春游,等待所有的员工到达集合地点才能出发,每个人到达后进入barrier状态。都到达后,唤起大家一起出发去旅行。

package MultiThreaded.dxc_sequence;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {

    static CyclicBarrier barrier1 = new CyclicBarrier(2);
    static CyclicBarrier barrier2 = new CyclicBarrier(2);

    public static void main(String[] args) throws InterruptedException {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("产品经理规划新需求");
                    //放开栅栏1
                    barrier1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //放开栅栏1
                    barrier1.await();
                    System.out.println("开发人员开发新需求功能");
                    //放开栅栏2
                    barrier2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //放开栅栏2
                    barrier2.await();
                    System.out.println("测试人员测试新功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("测试人员来上班了...");
        thread3.start();
        System.out.println("产品经理来上班了...");
        thread2.start();
        Thread.sleep(10);
        System.out.println("开发人员来上班了...");
        thread1.start();

    }
}