Java并發(fā)——設(shè)計(jì)模式

并發(fā)模式

Ⅰ 同步模式——保護(hù)性暫停模式(Guarded Suspension)

用在一個(gè)線程等待另一個(gè)線程的執(zhí)行結(jié)果。JDKjoinFuture的實(shí)現(xiàn)就是采用此模式

image-20201227134818869

實(shí)現(xiàn)代碼(不帶超時(shí)):

package com.ljh2.guardedSuspension;

/**
 *不帶超時(shí)
 * 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();
        }
    }

}

//測(cè)試
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帶超時(shí)的實(shí)現(xiàn)代碼:

public class GuardedSuspension2 {

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

    public Object get (long miles) {

        synchronized (lock) {
            //設(shè)置時(shí)間初始值
            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();
                }
                //剩余還需要等待的時(shí)間
                passedTime = System.currentTimeMillis()-begin;

            }
            return response;
        }
    }

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

    }

}

//測(cè)試
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 (猶豫)模式用在一個(gè)線程發(fā)現(xiàn)另一個(gè)線程或本線程已經(jīng)做了某一件相同的事,那么本線程就無需再做了,直接結(jié)束返回。可以用來實(shí)現(xiàn)線程安全的單例模式

public final class Singleton {
    private Singleton(){}
    //標(biāo)記是否有了實(shí)例
    private static Singleton INSTANCE = null;
    
    private static synchronized Singleton getInstance () {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        INSTANCE = new Singleton();
        return INSTANCE;
    }
}

Ⅲ 同步模型——順序控制

固定運(yùn)行順序

1、wait notify版本實(shí)現(xiàn)

public class SequenceControl1 {
    static final Object lock = new Object();
    //標(biāo)記t2是否已經(jīng)執(zhí)行
    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來實(shí)現(xiàn)(不需要上鎖和標(biāo)記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();

    }
}

交替打?。?/strong>

1、wait notify版本實(shí)現(xiàn)

需要另外構(gòu)造一個(gè)類

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 現(xiàn)在要打印的線程編號(hào)
     * @param nextFlag 下一個(gè)要打印的線程編號(hào)
     * @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();
            }
        }
    }
}

//測(cè)試
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();
            }
        }
    }
}

//測(cè)試
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版本實(shí)現(xiàn)

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

//測(cè)試
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();
    }
}

Ⅳ 異步模式——生產(chǎn)者/消費(fèi)者模式

  • 不同于前面的保護(hù)性暫停模式,它不需要產(chǎn)生結(jié)果和消費(fèi)結(jié)果的線程一一對(duì)應(yīng)
  • 生產(chǎn)者僅負(fù)責(zé)產(chǎn)生結(jié)果數(shù)據(jù),不關(guān)心數(shù)據(jù)該如何處理,而消費(fèi)者專心處理結(jié)果數(shù)據(jù)
  • JDK 中各種阻塞隊(duì)列,采用的就是這種模式
//消息對(duì)象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<>();
    }

    //消費(fèi)
    public Message take () {
        synchronized (messageQueue) {
            while (messageQueue.isEmpty()) {
                System.out.println("隊(duì)列空了,生產(chǎn)者快來生產(chǎn)。。。");
                try {
                    messageQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("消費(fèi)。。。");
            Message message = messageQueue.removeFirst();
            messageQueue.notifyAll();
            return message;
        }
    }

    public void put (Message message) {
        synchronized (messageQueue) {
            while (messageQueue.size() == capacity) {
                System.out.println("隊(duì)列滿了,消費(fèi)者快來消費(fèi)。。。");
                try {
                    messageQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("生產(chǎn)。。。");
            messageQueue.addLast(message);
            messageQueue.notifyAll();
        }
    }
}

//測(cè)試
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));
            },"生產(chǎn)者"+i).start();
        }

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

}

Ⅴ 兩階段終止模式

在一個(gè)線程 T1 中如何“優(yōu)雅”終止線程 T2?這里的【優(yōu)雅】指的是給 T2 一個(gè)料理后事的機(jī)會(huì)。注意代碼里面的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("保存結(jié)果");
                } catch (InterruptedException e) {
                    //睡眠被打斷后,讓線程打斷!!!
                    Thread.currentThread().interrupt();
                }
                //執(zhí)行監(jiān)控操作
            }
        },"監(jiān)控線程");
        thread.start();
    }

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

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

2、利用停止標(biāo)記(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("保存結(jié)果");
                } catch (InterruptedException e) {
                    //catch里面什么都不用做
                }
                //執(zhí)行監(jiān)控操作
            }
        },"監(jiān)控線程");
        thread.start();
    }

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

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

Ⅵ 線程安全的單例模式

注意是私有的構(gòu)造函數(shù)

image-20201228142927732

1、餓漢式單例(不利于節(jié)約資源)

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

2、懶漢式線程安全(對(duì)獲取對(duì)象實(shí)例的方法加鎖。鎖的粒度太大,性能不好)

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

3、懶漢式線程安全(雙重鎖校驗(yàn),使用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,可能發(fā)生重排序?yàn)棰佗邰?,還沒有初始化就分配地址了,因此此時(shí)的uniqueSingleton != null,直接就返回了沒有初始化的uniqueSingleton

    image-20201228145337408
  • 如果沒有第二個(gè)if語句,那么會(huì)發(fā)生兩次實(shí)例化

4、靜態(tài)內(nèi)部類實(shí)現(xiàn)

當(dāng) Singleton 類被加載時(shí),靜態(tài)內(nèi)部類 SingletonHolder 沒有被加載進(jìn)內(nèi)存。只有當(dāng)調(diào)用 getUniqueInstance() 方法從而觸發(fā) SingletonHolder.INSTANCE 時(shí) SingletonHolder 才會(huì)被加載,此時(shí)初始化 INSTANCE 實(shí)例,并且 JVM 能確保 INSTANCE 只被實(shí)例化一次。

這種方式不僅具有延遲初始化的好處,而且由 JVM 提供了對(duì)線程安全的支持。

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串池、數(shù)據(jù)庫連接池

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容