Java并发——设计模式

并发模式

Ⅰ 同步模式——保护性暂停模式(Guarded Suspension)

用在一个线程等待另一个线程的执行结果。JDKjoinFuture的实现就是采用此模式

image-20201227134818869

实现代码(不带超时):

package com.ljh2.guardedSuspension;

/**
 *不带超时
 * Created by LJH on 2020/12/27 13:54
 */
public class GuardedSuspension {

    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();
        }
    }

}

//测试
public class GuardedSuspensionTest1 {

    public static void main(String[] args) {
        GuardedSuspension guardedSuspension = new GuardedSuspension();
        new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            guardedSuspension.complete(null);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            guardedSuspension.complete("ljh");
        },"t1").start();

        Object o = guardedSuspension.get();
        System.out.println(o);
    }

}

get带超时的实现代码:

public class GuardedSuspension2 {

    private Object response;
    private final Object lock = new Object();

    public Object get (long miles) {

        synchronized (lock) {
            //设置时间初始值
            long begin = System.currentTimeMillis();
            long passedTime = 0;
            while (response == null) {
                long waitTime = miles-passedTime;
                if (waitTime <= 0 ){
                    System.out.println("break");
                    break;
                }
                try {
                    //不要忘记加入waitTime!!!
                    lock.wait(waitTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //剩余还需要等待的时间
                passedTime = System.currentTimeMillis()-begin;

            }
            return response;
        }
    }

    public void complete (Object response) {
        synchronized (lock) {
            this.response = response;
            lock.notifyAll();
        }

    }

}

//测试
public class GuardedSuspendTest2 {

    public static void main(String[] args) {
        GuardedSuspension2 guardedSuspension2 = new GuardedSuspension2();
        new Thread( () -> {
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            guardedSuspension2.complete(null);
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            guardedSuspension2.complete("ljh");
        },"t2").start();
        
        //1950ms来不及get到"ljh"
        Object o = guardedSuspension2.get(1950);
        System.out.println(o);
    }
}

Ⅱ 同步模式——犹豫模式(Balking)

Balking (犹豫)模式用在一个线程发现另一个线程或本线程已经做了某一件相同的事,那么本线程就无需再做了,直接结束返回。可以用来实现线程安全的单例模式

public final class Singleton {
    private Singleton(){}
    //标记是否有了实例
    private static Singleton INSTANCE = null;
    
    private static synchronized Singleton getInstance () {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        INSTANCE = new Singleton();
        return INSTANCE;
    }
}

Ⅲ 同步模型——顺序控制

固定运行顺序

1、wait notify版本实现

public class SequenceControl1 {
    static final Object lock = new Object();
    //标记t2是否已经执行
    static boolean flag = false;
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                if (!flag) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("1");
            }

        }, "t1");

        Thread t2 = new Thread(() -> {
            System.out.println("2");
            synchronized (lock) {
                flag = true;
                lock.notifyAll();
            }

        }, "t2");

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

2、park unpark来实现(不需要上锁和标记flag

public class SequenceControl2 {

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {

            LockSupport.park();
            System.out.println("1");

        }, "t1");

        Thread t2 = new Thread(() -> {

            System.out.println("2");
            LockSupport.unpark(t1);

        }, "t2");

        t1.start();
        t2.start();

    }
}

交替打印:

1、wait notify版本实现

需要另外构造一个类

public class SequenceControl3 {

    private int looptime;
    private int printItem;

    public SequenceControl3() {
    }

    public SequenceControl3(int looptime, int printItem) {
        this.looptime = looptime;
        this.printItem = printItem;
    }

    /**
     *
     * @param waitFlag 现在要打印的线程编号
     * @param nextFlag 下一个要打印的线程编号
     * @param str 打印的字符串
     */
    public void print (int waitFlag,int nextFlag,String str) {
        for (int i = 0; i < looptime; i++) {
            synchronized (this) {
                while (this.printItem != waitFlag) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print(str);
                printItem = nextFlag;
                this.notifyAll();
            }
        }
    }
}

//测试
public class Test {
    public static void main(String[] args) {
        SequenceControl3 sequenceControl3 = new SequenceControl3(5, 1);
        new Thread(()->{
            sequenceControl3.print(1,2,"a");
        },"t1").start();

        new Thread(()->{
            sequenceControl3.print(2,3,"b");
        },"t2").start();

        new Thread(()->{
            sequenceControl3.print(3,1,"c");
        },"t3").start();
    }
}

2、Lock条件变量

public class SequenceControl4_2 extends ReentrantLock {

    private int looptime;

    public SequenceControl4_2(int looptime) {
        this.looptime = looptime;
    }

    public void start (Condition condition) {
        this.lock();
        try{
            condition.signal();
        }finally {
            this.unlock();
        }
    }

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

//测试
public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        SequenceControl4_2 sequenceControl4_2 = new SequenceControl4_2(5);
        Condition condition1 = sequenceControl4_2.newCondition();
        Condition condition2 = sequenceControl4_2.newCondition();
        Condition condition3 = sequenceControl4_2.newCondition();

        new Thread(()->{
            sequenceControl4_2.print("a",condition1,condition2);
        },"t1").start();

        new Thread(()->{
            sequenceControl4_2.print("b",condition2,condition3);
        },"t2").start();

        new Thread(()->{
            sequenceControl4_2.print("c",condition3,condition1);
        },"t3").start();
        Thread.sleep(1000);
        sequenceControl4_2.start(condition1);
    }
}

3、park unpark版本实现

public class SequenceControl5 {

    private int looptime;
    private Thread[] threads;

    public SequenceControl5(int looptime) {
        this.looptime = looptime;
    }

    public void setThreads(Thread[] threads) {
        this.threads = threads;
    }

    public void print (String str) {
        for (int i = 0; i < looptime; i++) {
            LockSupport.park();
            System.out.print(str);
            LockSupport.unpark(nextThread());
        }
    }

    private Thread nextThread() {

        int index = 0;
        for (int i = 0; i < threads.length; i++) {
            if (threads[i] == Thread.currentThread()) {
                index = i;
                break;
            }
        }

        if (index < threads.length-1) {
            return threads[index+1];
        } else return threads[0];
    }

    void begin() {
        for (Thread thread : threads) {
            thread.start();
        }
        LockSupport.unpark(threads[0]);
    }
}

//测试
public class Test {
    public static void main(String[] args) {
        SequenceControl5 sequenceControl5 = new SequenceControl5(5);
        Thread t1 = new Thread(() -> {
            sequenceControl5.print("a");
        }, "t1");

        Thread t2 = new Thread(() -> {
            sequenceControl5.print("b");
        }, "t2");

        Thread t3 = new Thread(() -> {
            sequenceControl5.print("c");
        }, "t3");

        sequenceControl5.setThreads(new Thread[]{t1,t2,t3});
        sequenceControl5.begin();
    }
}

Ⅳ 异步模式——生产者/消费者模式

  • 不同于前面的保护性暂停模式,它不需要产生结果和消费结果的线程一一对应
  • 生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据
  • JDK 中各种阻塞队列,采用的就是这种模式
//消息对象Message类
public class Message {
    private int id;
    private Object message;

    public Message(int id, Object message) {
        this.id = id;
        this.message = message;
    }

    public int getId() {
        return id;
    }

    public Object getMessage() {
        return message;
    }
    
    @Override
    public String toString() {
        return "Message{" +
                "id=" + id +
                ", message=" + message +
                '}';
    }
}

//MessageQueeu
public class MessageQueue {

    private LinkedList<Message> messageQueue;
    private int capacity;

    public MessageQueue(int capacity) {
        this.capacity = capacity;
        messageQueue = new LinkedList<>();
    }

    //消费
    public Message take () {
        synchronized (messageQueue) {
            while (messageQueue.isEmpty()) {
                System.out.println("队列空了,生产者快来生产。。。");
                try {
                    messageQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("消费。。。");
            Message message = messageQueue.removeFirst();
            messageQueue.notifyAll();
            return message;
        }
    }

    public void put (Message message) {
        synchronized (messageQueue) {
            while (messageQueue.size() == capacity) {
                System.out.println("队列满了,消费者快来消费。。。");
                try {
                    messageQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("生产。。。");
            messageQueue.addLast(message);
            messageQueue.notifyAll();
        }
    }
}

//测试
public class Test {

    public static void main(String[] args) {
        MessageQueue messageQueue = new MessageQueue(2);
        for (int i = 0; i < 4; i++) {
            int id = i;
            new Thread(()->{
                messageQueue.put(new Message(id,"ljh"+id));
            },"生产者"+i).start();
        }

        new Thread(()->{
            while (true) {
                Message message = messageQueue.take();
            }
        },"消费者").start();
    }

}

Ⅴ 两阶段终止模式

在一个线程 T1 中如何“优雅”终止线程 T2?这里的【优雅】指的是给 T2 一个料理后事的机会。注意代码里面的catch

image-20201228111241839

1、利用isInterrupted

public class TwoPhaseTermination {
    private Thread thread;

    public void start() {
        thread = new Thread(()->{
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("料理后事");
                    break;
                }
                try {
                    Thread.sleep(1000);
                    System.out.println("保存结果");
                } catch (InterruptedException e) {
                    //睡眠被打断后,让线程打断!!!
                    Thread.currentThread().interrupt();
                }
                //执行监控操作
            }
        },"监控线程");
        thread.start();
    }

    public void stop() {
        thread.interrupt();
    }
}

//测试
public class Test {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination twoPhaseTermination = new TwoPhaseTermination();
        twoPhaseTermination.start();
        Thread.sleep(3500);
        twoPhaseTermination.stop();
    }
}

2、利用停止标记(volatile变量)

public class TwoPhaseTermination2 {

    private Thread thread;
    private volatile boolean flag = false;

    public void start() {
        thread = new Thread(()->{
            while (true) {
                if (flag) {
                    System.out.println("料理后事。。。");
                    break;
                }
                try {
                    Thread.sleep(1000);
                    System.out.println("保存结果");
                } catch (InterruptedException e) {
                    //catch里面什么都不用做
                }
                //执行监控操作
            }
        },"监控线程");
        thread.start();
    }

    public void stop() {
        flag = true;
        thread.interrupt();
    }
}

//测试
public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination2 twoPhaseTermination2 = new TwoPhaseTermination2();
        twoPhaseTermination2.start();
        Thread.sleep(3500);
        twoPhaseTermination2.stop();
    }
}

Ⅵ 线程安全的单例模式

注意是私有的构造函数

image-20201228142927732

1、饿汉式单例(不利于节约资源)

public class Singleton{
    private static Singleton uniqueSingleton = new Singleton();
    
    private Singleton(){}
    
    private Singleton getSingleton() {
        return uniqueSingleton;
    }
}

2、懒汉式线程安全(对获取对象实例的方法加锁。锁的粒度太大,性能不好)

public class Singleton{
    private static Singleton uniqueSingleton = null;
    
    private Singleton(){}
    
    private synchronized Singleton getSingleton() {
        if (uniqueSingleton == null) {
            uniqueSingleton = new Singleton();
        }
        return uniqueSingleton;
    }
}

3、懒汉式线程安全(双重锁校验,使用volatile,性能好)

public class Singleton{
    private volatile static Singleton uniqueSingleton = null;
    
    private Singleton(){}
    
    private Singleton getSingleton() {
        if (uniqueSingleton == null) {
            synchronized (this.class) {
                if (uniqueSingleton == null) {
                    uniqueSingleton = new Singleton();
                }
            }
        }
        return uniqueSingleton;
    }
}
  • 使用volatile的原因:保证可见性和有序性(禁止指令重排)

    uniqueSingleton = new Singleton();包括三部分:

    ① 为uniqueSingleton分配地址空间

    ② 初始化uniqueSingleton

    ③ 将uniqueSingleton指向分配的地址空间

    如果不用volatile,可能发生重排序为①③②,还没有初始化就分配地址了,因此此时的uniqueSingleton != null,直接就返回了没有初始化的uniqueSingleton

    image-20201228145337408
  • 如果没有第二个if语句,那么会发生两次实例化

4、静态内部类实现

当 Singleton 类被加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。

这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

public class Singleton {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getUniqueInstance() {
        return SingletonHolder.INSTANCE;
    }
}

5、使用枚举类

Ⅶ 享元模式

包装类、String串池、数据库连接池

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容