• JUC02-多场景下的线程同步操作


    场景一:两个线程进行数据传递,一个线程等待另一个线程的结果

    在实际业务场景中,我们大概率会使用Future的方式来得到该结果,但Future必须是当t2线程执行完返回结果后才能在t1线程获取到。但我们可能会碰到t2线程业务很重,而t1线程只需要等待t2完成某一个操作时就能得到该结果继续往下执行,我们该怎么办呢?

    保护-暂停模式

    核心思想:两个线程共享一个堆内对象,若该对象不满足条件则一直等待,若该对象满足条件则往下执行,t2执行让该对象满足条件。

    这个对象需要我们来设计,让他能完成使得两个线程之间的数据传递

    @Slf4j(topic = "c.TestGuardedObject")
    public class TestGuardedObject {
        public static void main(String[] args) {
            GuardedObject guardedObject = new GuardedObject();
            new Thread(() -> {
                try {
                    List response = download();
                    log.debug("download complete...");
                    guardedObject.complete(response);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
    
            log.debug("waiting...");
            Object response = guardedObject.get();
            log.debug("get response: [{}] lines", ((List) response).size());
    
        }
    
    
    }
    
    class GuardedObject {
    
        private Object response;
        private final Object lock = new Object();
    
        public Object get() {
            synchronized (lock) {
                // 条件不满足则等待
                while (response == null) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return response;
            }
        }
    
        public void complete(Object response) {
            synchronized (lock) {
                // 条件满足,通知等待线程
                this.response = response;
                lock.notifyAll();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    注意:当执行wait操作时将其包裹在while里面而不是if,是为了避免其被虚假唤醒而导致错误的向下执行

    场景二:生产者、消费者问题

    某类线程专门生产消息,某类线程专门消费消息,但消息与消费者之间并不是一一对应的关系,任意一个消费者都能消费任意一个消息

    核心思路:我们设计一个消息队列,生产消息的方法当队满时则阻塞,消费消息的方法当队空时则阻塞

    @Slf4j(topic = "c.MessageQueue")
    class MessageQueue {
        private LinkedList queue;
        private int capacity;
    
        public MessageQueue(int capacity) {
            this.capacity = capacity;
            queue = new LinkedList<>();
        }
    
        public Message take() {
            synchronized (queue) {
                while (queue.isEmpty()) {
                    log.debug("没货了, wait");
                    try {
                        queue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Message message = queue.removeFirst();
                queue.notifyAll();
                return message;
            }
        }
    
        public void put(Message message) {
            synchronized (queue) {
                while (queue.size() == capacity) {
                    log.debug("库存已达上限, wait");
                    try {
                        queue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                queue.addLast(message);
                queue.notifyAll();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    场景三:多线程下控制输出顺序

    业务场景:三个线程分别输出a,b,c。输入五次,要求交替输出,最后的结果是:abcabcabcabcabc。
    核心思想:设置一个公共整型变量status,每个线程对应status一个值,当status=它们对应的值的时候它们才能输出,我们将其封装为一个对象

    使用wait\notify

    class SyncWaitNotify {
        private int flag;
        private int loopNumber;
    
        public SyncWaitNotify(int flag, int loopNumber) {
            this.flag = flag;
            this.loopNumber = loopNumber;
        }
    
        public void print(int waitFlag, int nextFlag, String str) {
            for (int i = 0; i < loopNumber; i++) {
                synchronized (this) {
                    while (this.flag != waitFlag) {
                        try {
                            this.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print(str);
                    flag = nextFlag;
                    this.notifyAll();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    ReentrantLock

    @Slf4j(topic = "c.Test28")
    public class Test28 {
        public static void main(String[] args) {
            AwaitSignal2 as = new AwaitSignal2(3);
            as.start(new Thread(() -> {
                as.print("a");
            }), new Thread(() -> {
                as.print("b");
            }), new Thread(() -> {
                as.print("c");
            }), new Thread(() -> {
                as.print("d");
            }));
    
    
        }
    }
    
    @Slf4j(topic = "c.AwaitSignal")
    class AwaitSignal2 extends ReentrantLock {
        private Map map = new HashMap<>();
    
        public void start(Thread... threads) {
            Condition[] temp = new Condition[threads.length];
            for (int i = 0; i < threads.length; i++) {
                temp[i] = this.newCondition();
            }
            for (int i = 0; i < threads.length; i++) {
                Condition current = temp[i];
                Condition next;
                if (i == threads.length - 1) {
                    next = temp[0];
                } else {
                    next = temp[i + 1];
                }
                map.put(threads[i], new Condition[]{current, next});
            }
            for (Thread thread : map.keySet()) {
                thread.start();
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.lock();
            try {
                map.get(threads[0])[0].signal();
            } finally {
                this.unlock();
            }
        }
    
        public void print(String str) {
            for (int i = 0; i < loopNumber; i++) {
                this.lock();
                try {
                    Condition[] conditions = map.get(Thread.currentThread());
                    conditions[0].await();
                    log.debug(str);
                    conditions[1].signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    this.unlock();
                }
            }
        }
    
        // 循环次数
        private int loopNumber;
    
        public AwaitSignal2(int loopNumber) {
            this.loopNumber = loopNumber;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
  • 相关阅读:
    Spring Boot中使用Swagger3.0.0版本构建RESTful APIs
    bootstrap样式
    Manacher算法
    Python里的类型list是什么?
    使用Python+selenium实现第一个自动化测试脚本
    Go 函数的健壮性、panic异常处理、defer 机制
    基于AVDTP信令分析蓝牙音频启动流程
    Java代理模式
    3-2 中阶API示范
    数据结构: unordered_map与unordered_set
  • 原文地址:https://blog.csdn.net/qq_42861526/article/details/127699918