3、后记 - 同步模式之顺序控制 (控制线程操作顺序)

1、固定运行顺序

比如,必须先2后1打印

1.1、wait notify 版本

static Object obj = new Object();
    static boolean t2runed = false;
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (obj) {
                while(!t2runed) {
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(1);
            }

        }, "t1");
        Thread t2 = new Thread(() -> {
            System.out.println(2);
            synchronized (obj) {
                t2runed = true;
                obj.notifyAll();
            }
        },"t2");
        t1.start();
        t2.start();
    }

输出

2
1

1.2、park unpark 版本

可以看到,上面的实现很麻烦:

  • 首先,需要保证先wait在notify,否则wait线程永远得不到唤醒,因此使用了【运行标记】来判断该不该wait
  • 第二,如果有些干扰线程错误地notify了wait线程,条件不满足时还需要 重新等待,使用while循环来解决此问题
  • 最后,唤醒对象上的wait线程需要使用notifyAll,因为【同步对象】上等待的线程可能不止一个

可以使用LockSupport类的park和unpark来简化上面的题目;

public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LockSupport.park();
            System.out.println(1);
        }, "t1");
        Thread t2 = new Thread(() -> {
            System.out.println(2);
            LockSupport.unpark(t1);
        }, "t2");
        t1.start();
        t2.start();
    }

park和unpark方法比较灵活,他们谁先调用,谁后调动无所谓,并且是以线程为单位进行【暂停】和【恢复】,不需要【同步对象】和【运行标记】

2、交替输出

线程1 输出a 5次,线程2 输出b 5次,线程3 输出 c 5次,现在要求输出abcabcabcabcabc怎么实现

2.1、wait notify版本

public class StepControlV3 {

    public static void main(String[] args) {
        SyncWaitNotify syncWaitNotify = new SyncWaitNotify(1, 5);
        Thread t1 = new Thread(() -> {
            syncWaitNotify.print(1, 2, "a");
        }, "t1");
        Thread t2 = new Thread(() -> {
            syncWaitNotify.print(2, 3, "b");
        }, "t2");
        Thread t3 = new Thread(() -> {
            syncWaitNotify.print(3, 1, "c");
        }, "t3");
        t1.start();
        t2.start();
        t3.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println();

        SyncWaitNotify2 syncWaitNotify2 = new SyncWaitNotify2(5, 3);
        Thread t4 = new Thread(() -> {
            syncWaitNotify2.print(1, "a");
        }, "t4");
        Thread t5 = new Thread(() -> {
            syncWaitNotify2.print(2, "b");
        }, "t5");
        Thread t6 = new Thread(() -> {
            syncWaitNotify2.print(0, "c");
        }, "t6");
        t4.start();
        t5.start();
        t6.start();
    }

}

class SyncWaitNotify {

    int loopTime = 0;
    int flg = 0;

    public SyncWaitNotify(int flg, int loopTime) {
        this.flg = flg;
        this.loopTime = loopTime;
    }

    public void print(int waitFlg, int nextFlg, String str) {
        for (int i = 0; i < loopTime; i++) {
            synchronized (this) {
                while (flg != waitFlg) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(str);
                flg = nextFlg;
                this.notifyAll();
            }
        }
    }

}

class SyncWaitNotify2 {

    int loopTime = 0;

    int cnt = 0;
    int threadCnt = 0;

    public SyncWaitNotify2(int loopTime, int threadCnt) {
        this.loopTime = loopTime;
        this.threadCnt = threadCnt;
    }

    public void print(int waitFlg, String str) {
        for (int i = 0; i < loopTime; i++) {
            synchronized (this) {
                while ((cnt + 1) % 3 != waitFlg) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(str);
                cnt++;
                this.notifyAll();
            }
        }
    }

}

2.2、Lock条件变量版本

public class StepControlV4 {

    public static void main(String[] args) {
        AwaitSignal awaitSignal = new AwaitSignal(5);
        Condition aCondition = awaitSignal.newCondition();
        Condition bCondition = awaitSignal.newCondition();
        Condition cCondition = awaitSignal.newCondition();
        Thread t1 = new Thread(() -> {
            awaitSignal.print("a", aCondition, bCondition);
        }, "t1");
        Thread t2 = new Thread(() -> {
            awaitSignal.print("b", bCondition, cCondition);
        }, "t2");
        Thread t3 = new Thread(() -> {
            awaitSignal.print("c", cCondition, aCondition);
        }, "t3");
        t1.start();
        t2.start();
        t3.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        awaitSignal.start(aCondition);
    }

}

@Slf4j(topic = "c.AwaitSignal")
class AwaitSignal extends ReentrantLock {

    int loopTime;

    public AwaitSignal(int loopTime) {
        this.loopTime = loopTime;
    }

    public void start(Condition first) {
        this.lock();
        try {
            log.debug("start");
            first.signal();
        } finally {
            this.unlock();
        }

    }

    public void print(String str, Condition cur, Condition next) {
        for (int i = 0; i < loopTime; i++) {
            this.lock();
            try {
                cur.await();
                log.debug(str);
                next.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                this.unlock();
            }
        }

    }
    
}

2.3、park unpark 版本

@Slf4j(topic = "c.StepControlV5")
public class StepControlV5 {
    public static void main(String[] args) {
        SyncPark syncPark = new SyncPark(5);
        Thread t1 = new Thread(() -> {
            syncPark.print("a");
        }, "t1");
        Thread t2 = new Thread(() -> {
            syncPark.print("b");
        }, "t2");
        Thread t3 = new Thread(() -> {
            syncPark.print("c");
        }, "t3");
        syncPark.setThreads(new Thread[]{t1, t2, t3});
        syncPark.start();
    }
}

@Slf4j(topic = "c.SyncPark")
class SyncPark {
    int loopTime;
    List<Thread> threads;
    public SyncPark(int loopTime) {
        this.loopTime = loopTime;
    }
    public void setThreads(Thread[] threads) {
        this.threads = Arrays.asList(threads);
    }
    public void start() {
        for (Thread thread : threads) {
            thread.start();
        }
        LockSupport.unpark(threads.get(0));
    }
    public void print(String str) {
        for (int i = 0; i < loopTime; i++) {
            LockSupport.park();
            log.debug(str);
            LockSupport.unpark(nextThread());
        }
    }
    public Thread nextThread() {
        Thread currentThread = Thread.currentThread();
        int index = threads.indexOf(currentThread);
        return index == (threads.size() - 1) ? threads.get(0) : threads.get(index + 1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值