美文网首页
并发编程(七):多线程设计模式

并发编程(七):多线程设计模式

作者: codeMover | 来源:发表于2020-04-14 23:14 被阅读0次

同步模式-保护性暂停

  • 定义:一个线程等待另一个线程的执行结果
  • 要点:
    • 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个GuardedObject
    • 如果有结果不断从一个线程到另一个线程可以使用消息队列
    • JDK中,join的实现、future的实现,采用的保护性暂停
    • 因为要等待另一方的结果,因此归类到同步模式


      保护性暂停.png
  • 优点:
    • 使用join()必须等待另外一个线程结束,才能继续执行,保护性暂停可以在对象中设置值后唤醒等待的线程,继续下面操作
    • 使用join,等待变量必须设置为全局的,采用保护性暂停可以设置为局部
@Slf4j(topic = "ants.ProtectiveSuspension")
public class ProtectiveSuspension {
    public static void main(String[] args) {
        GuardObject guardObject = new GuardObject();

        new Thread("t1"){
            @SneakyThrows
            @Override
            public void run() {
                log.debug("t1获取值");
                log.debug("保护性暂停获得值:{}",guardObject.get(2000));
            }
        }.start();
        new Thread("t2"){
            @SneakyThrows
            @Override
            public void run() {
                log.debug("t2设置值");
                Thread.sleep(2000);

                guardObject.set(1);
            }
        }.start();
    }
}

class GuardObject {
    private Object object;

    public Object get(long time) throws InterruptedException {
        // 虚假唤醒
        synchronized (this) {
            long beginTime = System.currentTimeMillis();
            long timeWait = 0;
            while (object == null&&timeWait<= time) {
                this.wait(time-timeWait);
                timeWait=System.currentTimeMillis()-beginTime;
            }
        }
        return object;
    }

    public void set(Object o) {
        synchronized (this) {
            this.object = o;
            this.notifyAll();

        }
    }
}

join()

  • 在Thread中的join方法,使用保护性暂停来实现。

异步模式-生产者/消费者

  • 要点
    • 与保护性暂停不一样,不需要产生结果和消费结果的线程一一对应
    • 消费队列可以用来平衡生产和消费的线程资源
    • 生产者仅负责生产数据,不关心数据该如何处理,而消费者专心处理结果数据
    • 消息队列是由容量限制的,满时不会加入数据,空时不会再消耗数据
    • JDK中的各种阻塞队列,采用的就是这种模式
生产者和消费者模式.png

Park & Unpark

  • park和unpark是LockSupport类中的方法
  • LockSupport.park(); 暂停当前线程
  • LockSupport.unpark(暂停线程对象); 恢复某个线程的运行
  • unpark()可以在park()之前或之后运行,都可以让wait的线程继续执行
  • 与Object的wait&notify相比
    • wait、notify和notify必须配合Object Monitor一起使用,而park和unpark不必
    • park和unpark是以线程为单位来【阻塞】和【唤醒】线程,而notify只能随机唤醒一个等待线程,notifyAll是唤醒所有等待的线程,不能很准确的唤醒指定线程
    • park&unpark中unpark可以先执行,而wait&notify中不能限制性notify
@Slf4j(topic = "ants.TestParkUnpark")
public class TestParkUnpark {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("t1") {
            @SneakyThrows
            @Override
            public void run() {
                log.debug("park 之前代码");
                Thread.sleep(1000);
                LockSupport.park();
                log.debug("park 后面代码");
            }
        };
        t1.start();
        Thread.sleep(2000);
        log.debug("main 调用unpark");
        LockSupport.unpark(t1);
    }
}

park&unpark原理

  • 每个线程都有自己的一个park对象,由三部分组成_counter,_cond和_mutex
    • parker对象:每个先传给你都有自己的parker对象
    • _counter 状态只有0或1
    • _mutex 对象,里面有等待队列(_cond)
    • _cond 条件变量
image.png
  1. 当前线程调用Unsafe.park()方法
  2. 检查_counter,本情况为0,这是,获得_mutex互斥锁
  3. 线程进入_cond条件变量
  4. 设置_counter=0


    image.png
  5. 调用Unsafe.unpark(Thread_0)方法,设置_counter为1
  6. 唤醒_cond条件变量中的Therad_0
  7. Thread_0回复运行
  8. 设置_counter为0


    image.png
  9. 调用Unsafe.unpark(Thread_0)方法,设置_counter为1
  10. 当前线程调用Unsafe.park()方法
  11. 检查_counter,本情况为1,这时线程无需阻塞,继续运行
  12. 设置_counter为0

相关文章

网友评论

      本文标题:并发编程(七):多线程设计模式

      本文链接:https://www.haomeiwen.com/subject/nszxvhtx.html