简介

在实际多线程运用时,我们可能需要多个线程之间同步运行,如线程1线运行完,线程2再运行

线程固定顺序运行

  • 两个线程保证:
  • 2线程先打印b
  • 1线程再打印a

wait/notify实现

设置一个Boolean变量,如果为假,让其中一个线程自旋阻塞

/**
 * 两个线程保证:
 * 2线程先打印b
 * 1线程再打印a
 *
 * wait/notify实现
 */
public class TwoThread {
    static final Object lock = new Object();
    static boolean t2Run = false;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock){
                while(!t2Run){ //此处不可为if(!t2Run),否则会出现假唤醒的情况,即唤醒后会在执行完lock.wait()停止了,不会执行下面的代码
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+":a");
            }
        },"t1");

        Thread t2 = new Thread(() -> {
            synchronized (lock){
                System.out.println(Thread.currentThread().getName()+":b");
                t2Run = true;
                lock.notify();
            }
        },"t2");
        t1.start();
        t2.start();
    }
}

park/unpark实现(简单)

  • LockSupport就是通过控制变量_counter来对线程阻塞唤醒进行控制的。原理有点类似于信号量机制。
  • 当调用park()方法时,会将_counter置为0,同时判断前值,等于1说明前面被unpark过,则直接退出,否则将使该线程阻塞。
  • 当调用unpark()方法时,会将_counter置为1,同时判断前值,小于1会进行线程唤醒,否则直接退出。
  • 形象的理解,线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。当调用park方法时,如果有凭证,则会直接消耗掉这个凭证然后正常退出;
  • 但是如果没有凭证,就必须阻塞等待凭证可用;而unpark则相反,它会增加一个凭证,但凭证最多只能有1个。
  • 为什么可以先唤醒线程后阻塞线程?
  • 因为unpark获得了一个凭证,之后调用park因为有凭证消费,故不会阻塞。
  • 为什么唤醒两次后阻塞两次会阻塞线程。
  • 因为凭证的数量最多为1,连续调用两次unpark和调用一次unpark效果一样,只会增加一个凭证;而调用两次park却需要消费两个凭证。
public class TwoThredP2 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + ":a");
        }, "t1");

        Thread t2 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":b");
            LockSupport.unpark(t1);
        },"t2");
        t1.start();
        t2.start();
    }
}

ReentrantLock实现

public class TwoThread3 {
    static ReentrantLock reentrantLock = new ReentrantLock();
    static Condition conditionA = reentrantLock.newCondition();

    static boolean t2Run = false;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
           reentrantLock.lock();
           try{
               while (!t2Run){
                   try {
                       conditionA.await();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
               System.out.println(Thread.currentThread().getName()+":a");
           }finally {
               reentrantLock.unlock();
           }

        },"t1");

        Thread t2 = new Thread(() -> {
            reentrantLock.lock();
            try{

                System.out.println(Thread.currentThread().getName()+":b");
                t2Run = true;
                conditionA.signal();
            }finally {
                reentrantLock.unlock();
            }

        },"t2");
        t1.start();
        t2.start();
    }
}

线程交替运行

  • 实现三个线程按顺序循环打印abc
  • 1打印a
  • 2打印b
  • 3打印c

wait/notify

  • 思路:设置一个参数标志flag,为1时1执行并将flag置为2,2时2执行并将flag置为3,为3时3执行并将flag置为1
public class ThreeThread1 {


    public static void main(String[] args) {
        WaitNotify waitNotify = new WaitNotify(5);
        new Thread(()->{
            waitNotify.printf(1,2,"a");
        },"t1").start();
        new Thread(()->{
            waitNotify.printf(2,3,"b");
        },"t2").start();
        new Thread(()->{
            waitNotify.printf(3,1,"c");
        },"t3").start();
    }
}
class WaitNotify{
    private int loopNum ;
    private int flag = 1;

    public WaitNotify(int loopNum) {
        this.loopNum = loopNum;
    }

    public int getLoopNum() {
        return loopNum;
    }

    public void setLoopNum(int loopNum) {
        this.loopNum = loopNum;
    }

    public int getFlag() {
        return flag;
    }

    public void setFlag(int flag) {
        this.flag = flag;
    }

    public void printf(int currentFlag,int nextFlag,String str){
        for(int i=0;i<loopNum;i++){
            synchronized (this){
                while (currentFlag!=flag){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.printf(str);
                flag = nextFlag;
                this.notifyAll();//此处不可使用notify,因为notify是随机唤醒
            }
        }
    }
}

park/unpark

public class ThreeThread2 {

    static Thread t1 ;
    static Thread t2 ;
    static Thread t3 ;
    public static void main(String[] args) {
        ParkUnpark parkUnpark = new ParkUnpark(5);
        t1 = new Thread(()->{
            parkUnpark.printf("a",t2);
        },"t1");
        t2 = new Thread(()->{
            parkUnpark.printf("b",t3);
        },"t2");
        t3 = new Thread(()->{
            parkUnpark.printf("c",t1);
        },"t3");

        t1.start();
        t2.start();
        t3.start();
        LockSupport.unpark(t1);

    }
}
class ParkUnpark{

    private int loopNum;
    public ParkUnpark(int loopNum){
        this.loopNum = loopNum;
    }

    public void printf(String str,Thread next){
        for (int i = 0;i<loopNum;i++){
//            System.out.printf(Thread.currentThread().getName());
            LockSupport.park();
            System.out.printf(str);
            LockSupport.unpark(next);

        }
    }
}

ReentrantLock

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ThreeThread3 {
    public static void main(String[] args) {
        Rlock rlock = new Rlock(5);
        Condition a = rlock.newCondition();
        Condition b = rlock.newCondition();
        Condition c = rlock.newCondition();
        Thread t1 = new Thread(()->{
           rlock.printf("a",a,b);
        });
        Thread t2 = new Thread(()->{
            rlock.printf("b",b,c);
        });
        Thread t3 = new Thread(()->{
            rlock.printf("c",c,a);
        });
        t1.start();
        t2.start();
        t3.start();
        try {
            Thread.sleep(1000);//此处必须先让主线程沉睡一会,避免主线程先进入唤醒,t1再沉睡,导致死锁
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        rlock.lock();
        try{
            a.signal();
        }finally {
            rlock.unlock();
        }

    }
}
class Rlock extends ReentrantLock{
    private int loopNum;
    public Rlock(int loopNum){
        this.loopNum = loopNum;
    }

    public void printf(String str, Condition current,Condition next){
        for(int i=0;i<loopNum;i++){
            this.lock();
            try {
                try {
                    current.await();
                    System.out.printf(str);
                    next.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
                this.unlock();
            }
        }
    }
}