4. Java并發(fā)編程基礎(chǔ)

1. 線程簡(jiǎn)介

1.1 什么是線程

線程是現(xiàn)代操作系統(tǒng)能夠進(jìn)行調(diào)度和運(yùn)算的基本單位

在一個(gè)進(jìn)程中可以創(chuàng)建多個(gè)線程, 這些線程都有各自的計(jì)數(shù)器, 堆棧和局部變量等屬性, 并且能夠訪問(wèn)共享的內(nèi)存變量, CPU在這些線程上高速切換, 讓使用者感覺(jué)到這些線程在同時(shí)執(zhí)行

1.2 .為什么使用多線程
  1. 處理器核心數(shù)量越來(lái)越多, 超線程技術(shù)的廣泛應(yīng)用
  2. 更快的影響時(shí)間, 多線程處理復(fù)雜業(yè)務(wù)
  3. 提高CPU利用率
1.3 線程優(yōu)先級(jí)

現(xiàn)代操作系統(tǒng)基本采用時(shí)分的形式調(diào)度運(yùn)行的線程, 操作系統(tǒng)會(huì)分出一個(gè)個(gè)時(shí)間片, 線程會(huì)分配到若干時(shí)間片, 當(dāng)線程的時(shí)間片用完了就會(huì)發(fā)生線程調(diào)度, 并等待下次分配

線程分配到的時(shí)間片多少也就決定了線程使用處理器資源的多少, 線程優(yōu)先級(jí)作用是控制分配給線程的時(shí)間片多一些或少一些

在java中, 通過(guò)priority屬性來(lái)控制線程優(yōu)先級(jí), 優(yōu)先級(jí)范圍1~10, 默認(rèn)優(yōu)先級(jí)為5, 優(yōu)先級(jí)高的線程分配時(shí)間片的數(shù)量要多余優(yōu)先級(jí)低的線程

在不同的JVM以及操作系統(tǒng)上, 線程調(diào)度會(huì)存在差異, 有些操作系統(tǒng)甚至?xí)雎詫?duì)線程優(yōu)先級(jí)的設(shè)定, 所以優(yōu)先級(jí)不能作為程序正確性的依賴

1.4 多線程缺點(diǎn)
  • 線程的管理以及線程上下文切換占用額外資源
  • 線程導(dǎo)致的死鎖,共享資源的競(jìng)爭(zhēng)等操作導(dǎo)致等待時(shí)間過(guò)長(zhǎng)
  • 相應(yīng)的線程不安全的問(wèn)題
1.5 線程的生命周期
thread_baisc_life.png
狀態(tài)名稱 說(shuō)明
新建狀態(tài)(new) 線程被構(gòu)建, 還沒(méi)有調(diào)用start()方法
就緒狀態(tài)(runnable) 調(diào)用start()方法之后, 操作系統(tǒng)調(diào)度, 準(zhǔn)備就緒
運(yùn)行狀態(tài)(running) 操作系統(tǒng)執(zhí)行線程
阻塞狀態(tài)(blocked) 線程阻塞于鎖
等待狀態(tài)(waiting) 線程等待其他線程通知或者中斷等操作
超時(shí)等待狀態(tài)(time_waiting) 等待其他線程通知或者中斷等操作, 可以超時(shí)自動(dòng)返回
終止?fàn)顟B(tài)(terminated) 線程執(zhí)行完畢
各種狀態(tài)對(duì)應(yīng)的java代碼
  1. 阻塞狀態(tài)
    • 等待進(jìn)入synchronized代碼塊和方法
  2. 等待狀態(tài)和喚醒
    • Object.wait()和Object.notify()
    • Thread.join()
    • LockSupport.park()和LockSupport.unpark(Thread)
  3. 等待超時(shí)
    • Thread.sleep(long)
    • Object.wait(long)
    • Thread.join(long)
    • LockSupport.parkNanos()
    • LockSupport.parkUntil()
1.6 Daemon線程

Daemon線程是一種支持型線程, 主要被用作程序中后臺(tái)調(diào)度以及支持型工作

當(dāng)一個(gè)虛擬機(jī)只有Daemon線程的時(shí)候, java虛擬機(jī)將會(huì)被退出, 所以Daemon線程的finally語(yǔ)句不一定會(huì)執(zhí)行

Thread.setDaemon(true)將線程設(shè)置為Daemon線程

public class DaemonExample {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //線程sleep 1秒
                    //這時(shí)候main函數(shù)已經(jīng)執(zhí)行完成, JVM發(fā)現(xiàn)沒(méi)有非Daemon線程
                    //JVM直接退出, 而不會(huì)執(zhí)行finally語(yǔ)句
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("DeamonThread finally finish.");
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
    }
}

2. 實(shí)現(xiàn)多線程的三種方法

  • 繼承Thread類
  • 實(shí)現(xiàn)Runnable接口
  • 實(shí)現(xiàn)Callable接口,配合FutureTask類
2.1 繼承Thread類
//繼承Thread接口
public class ThreadTest extends Thread {

    @Override
    public void run() {
        System.out.println("hello,thread!");
    }

    public static void main(String[] args) {
        Thread t1 = new ThreadTest();
        t1.start();
    }
}
運(yùn)行結(jié)果
hello,thread!

2.2 實(shí)現(xiàn)Runnable接口

//實(shí)現(xiàn)Runable接口
public class RunableTest implements Runnable {
    @Override
    public void run() {
        System.out.println("hello,runnable!");
    }
    public static void main(String[] args) {
        RunableTest r = new RunableTest();
        Thread t2 = new Thread(r);
        t2.start();
    }
}
運(yùn)行結(jié)果
hello,runnable!
2.3 實(shí)現(xiàn)Callable接口
//實(shí)現(xiàn)Callable接口
public class CallableTest implements Callable<String> {

    @Override
    public String call() throws Exception {
        return "hello,callable!";
    }

    public static void main(String[] args) {
        CallableTest callable = new CallableTest();
        //使用FutureTask,可以獲取多線程返回值
        RunnableFuture<String> rf = new FutureTask<>(callable);
        //線程start()
        Thread t3 = new Thread(rf);
        t3.start();
        try {
            System.out.println(rf.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
運(yùn)行結(jié)果
hello,callable!
FutureTask的繼承關(guān)系
jdk_callable_future.png
//FutureTask實(shí)現(xiàn)了Runnable接口
//所以使用Thread來(lái)處理
public class FutureTask<V> implements RunnableFuture<V> {
    /**
     * 構(gòu)造一個(gè)FutureTask對(duì)象
     * @param callable 傳入的callable實(shí)例
     */
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW; 
    }
}
三種實(shí)現(xiàn)方式的聯(lián)系與區(qū)別
  1. Thread類繼承了Runnable接口
  2. Thread類使用了裝飾者模式,內(nèi)部的run方法實(shí)際是調(diào)用的是Runnable的run方法
  3. RunnableFuture繼承了Runnable和Future接口
  4. 實(shí)例化一個(gè)Thread對(duì)象,然后調(diào)用其start方法,線程進(jìn)入就緒狀態(tài)
2.4 線程中斷

中斷可以理解為線程的一個(gè)標(biāo)識(shí)位屬性, 表示一個(gè)運(yùn)行中的線程是否被其他線程進(jìn)行了中斷操作 可以使用那么interrupt()方法進(jìn)行復(fù)位

interrupt()方法實(shí)際作用是, 給受阻塞的線程ThreadA設(shè)置一個(gè)中斷信號(hào), 在線程ThreadB中, 調(diào)用了ThreadA.interrupt()方法, 其執(zhí)行順序如下

  • 如果線程ThreadA沒(méi)有被阻塞, 那么interrupt()方法將不會(huì)起作用, 當(dāng)線程ThreadA執(zhí)行到wait(), sleep(), join()方法時(shí), 才會(huì)拋出InterruptedException, 然后退出阻塞狀態(tài)
  • 如果線程ThreadA被阻塞 那么被執(zhí)行interrupt()方法的線程會(huì)拋出InterruptedException, 然后退出阻塞狀態(tài)
  • 線程的sleep(), join()和Object#wait方法會(huì)不斷檢查線程中斷狀態(tài)的值
2.4.1 sleep和interrupt
public class InterruptDemo {
    // 兩個(gè)全局線程對(duì)象, threadB用來(lái)interrupt ThreadA
    static Thread threadA = new Thread(new ThreadA());
    static Thread threadB = new Thread(new ThreadB());

    static Logger logger = Logger.getLogger("InterruptDemo");

    public static void main(String[] args) {
        threadA.start();
        threadB.start();
    }

    // 線程A, 先f(wàn)or循環(huán)一秒鐘, 在sleep一秒鐘
    // 觀察interrupt()之后的執(zhí)行順序
    static class ThreadA implements Runnable {
        @Override
        public void run() {
            logger.info("threadA start, isInterrupted = " + threadA.isInterrupted());
            // threadA for循環(huán)一秒鐘
            long startTime = System.currentTimeMillis();
             // threadA for循環(huán)一秒鐘
            long startTime = System.currentTimeMillis();
            for (;;)
                if (System.currentTimeMillis() - startTime > 1000) break;
            logger.info("after for loop, threadA isInterrupted = " + threadA.isInterrupted());
            // threadA sleep 一秒鐘
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                logger.severe("threadA InterruptedException " + e.getMessage() + ", isInterrupted = "
                        + threadA.isInterrupted());
            }
            logger.info("after threadA sleep isInterrupted = " + threadA.isInterrupted());
        }

    }

    // 線程B, 用來(lái)中斷線程A
    static class ThreadB implements Runnable {
        @Override
        public void run() {
            logger.info("before threadB interrupt threadA");
            // 中斷threadA
            threadA.interrupt();
            logger.info("after threadB interrupt threadA, threadA isInterrupted = " + threadA.isInterrupted());

        }
    }
}

執(zhí)行結(jié)果

10:57:07 thread.InterruptDemo$ThreadA run
信息: threadA start, isInterrupted = false
10:57:07 thread.InterruptDemo$ThreadB run
信息: before threadB interrupt threadA
10:57:07 thread.InterruptDemo$ThreadB run
信息: after threadB interrupt threadA, threadA isInterrupted = true
10:57:08 thread.InterruptDemo$ThreadA run
信息: after for loop, threadA isInterrupted = true
10:57:08 thread.InterruptDemo$ThreadA run
嚴(yán)重: threadA InterruptedException sleep interrupted, isInterrupted = false
10:57:08 thread.InterruptDemo$ThreadA run
信息: after threadA sleep isInterrupted = false

結(jié)果分析

  1. threadA開(kāi)始執(zhí)行的時(shí)候, isInterrupted默認(rèn)為false
  2. threadB執(zhí)行了threadA.interrupte()之后, threadA.isInterrupted()被設(shè)置為true, threadA處于中斷狀態(tài)
  3. 此時(shí)線程A繼續(xù)執(zhí)行for循環(huán), 并沒(méi)有拋出InterruptedException
  4. 然后在threadA.sleep()時(shí)候, 拋出InterruptedException, 并將isInterrupted復(fù)位為false
  5. threadA并沒(méi)有進(jìn)入阻塞狀態(tài), 而是繼續(xù)執(zhí)行后續(xù)代碼
2.4.2 join和interrupt
public class InterruptDemo {
    // 兩個(gè)全局線程對(duì)象, threadB用來(lái)interrupt ThreadA
    static Thread threadA = new Thread(new ThreadA());
    static Thread threadB = new Thread(new ThreadB());

    static Logger logger = Logger.getLogger("InterruptDemo");

    public static void main(String[] args) {
        threadA.start();
        threadB.start();
    }

    // threadA啟動(dòng)后執(zhí)行threadB.join()
    // 等待threadB執(zhí)行完成, threadA再繼續(xù)執(zhí)行
    static class ThreadA implements Runnable {
        @Override
        public void run() {
            logger.info("threadA start, isInterrupted = " + threadA.isInterrupted());
            try {
                //threadA會(huì)等待threadB執(zhí)行完成, 再繼續(xù)執(zhí)行
                threadB.join();
            } catch (InterruptedException e) {
                logger.severe("threadA InterruptedException " + e.getMessage() + ", isInterrupted = "
                        + threadA.isInterrupted());
            }
            // threadA for循環(huán)一秒鐘
            long startTime = System.currentTimeMillis();
            for (;;)
                if (System.currentTimeMillis() - startTime > 1000) break;
            logger.info("after for loop, threadA isInterrupted = " + threadA.isInterrupted());
        }

    }

    // 線程B, 用來(lái)中斷線程A
    static class ThreadB implements Runnable {
        @Override
        public void run() {
            logger.info("before threadB interrupt threadA");
            // 中斷threadA
            threadA.interrupt();
            logger.info("after threadB interrupt threadA, threadA isInterrupted = " + threadA.isInterrupted());
        }
    }
}

執(zhí)行結(jié)果

10:57:07 thread.InterruptDemo$ThreadA run
信息: threadA start, isInterrupted = false
10:57:07 thread.InterruptDemo$ThreadB run
信息: before threadB interrupt threadA
10:57:07 thread.InterruptDemo$ThreadB run
信息: after threadB interrupt threadA, threadA isInterrupted = true
10:57:08 thread.InterruptDemo$ThreadA run
嚴(yán)重: threadA InterruptedException null, isInterrupted = false
10:57:08 thread.InterruptDemo$ThreadA run
信息: after for loop, threadA isInterrupted = false

結(jié)果分析

  1. threadA開(kāi)始執(zhí)行的時(shí)候, isInterrupted默認(rèn)為false
  2. threadA執(zhí)行threadB.join()之后, 會(huì)等待threadB執(zhí)行結(jié)束,
  3. threadB執(zhí)行了threadA.interrupte()之后, threadA.isInterrupted()被設(shè)置為true, threadA于中斷狀態(tài)
  4. 此時(shí)theadA處于等待狀態(tài), 拋出InterruptedException, 并將isInterrupted復(fù)位為false
  5. threadA沒(méi)有等待threadB執(zhí)行完成, 而是立即恢復(fù), 繼續(xù)執(zhí)行后續(xù)代碼
2.4.3 wait和interrupt
public class InterruptDemo {
    // 兩個(gè)全局線程對(duì)象, threadB用來(lái)interrupt ThreadA
    static Thread threadA = new Thread(new ThreadA());
    static Thread threadB = new Thread(new ThreadB());

    static Logger logger = Logger.getLogger("InterruptDemo");

    //lock鎖
    static Object lock = new Object();

    public static void main(String[] args) {
        threadA.start();
        threadB.start();
    }

    // 線程A, 
    static class ThreadA implements Runnable {
        @Override
        public void run() {
            logger.info("threadA start, isInterrupted = " + threadA.isInterrupted());
            synchronized (lock) {
                try {
                    //等待其他線程喚醒
                    lock.wait();
                } catch (InterruptedException e) {
                    logger.severe("threadA InterruptedException " + e.getMessage() + ", isInterrupted = "
                            + threadA.isInterrupted());
                }
                logger.info("after threadA wait, isInterrupted = " + threadA.isInterrupted());

            }
        }
    }

    // 線程B, 用來(lái)中斷線程A
    static class ThreadB implements Runnable {
        @Override
        public void run() {
            logger.info("before threadB interrupt threadA");
            // 中斷threadA
            threadA.interrupt();
            logger.info("after threadB interrupt threadA, threadA isInterrupted = " + threadA.isInterrupted());
        }
    }
}

執(zhí)行結(jié)果

10:57:07 thread.InterruptDemo$ThreadA run
信息: threadA start, isInterrupted = false
10:57:07 thread.InterruptDemo$ThreadB run
信息: before threadB interrupt threadA
10:57:07 thread.InterruptDemo$ThreadB run
信息: after threadB interrupt threadA
10:57:08 thread.InterruptDemo$ThreadA run
嚴(yán)重: after threadB interrupt threadA, threadA isInterrupted = true
10:57:08 thread.InterruptDemo$ThreadA run
信息: after threadA wait, isInterrupted = false

結(jié)果分析

  1. threadA開(kāi)始執(zhí)行的時(shí)候, isInterrupted默認(rèn)為false
  2. threadA鎖住lock對(duì)象, 然后執(zhí)行l(wèi)ock.wait(), threadA進(jìn)入等待狀態(tài), 等待其他線程喚醒
  3. threadB執(zhí)行了threadA.interrupte()之后, threadA.isInterrupted()被設(shè)置為true, threadA處于中斷狀態(tài)
  4. 此時(shí)theadA處于等待狀態(tài), 拋出InterruptedException, 并將isInterrupted復(fù)位為false
  5. threadA恢復(fù)執(zhí)行
2.4.4 線程終結(jié)和interrupt

如果一個(gè)線程threadA已經(jīng)處于終結(jié)狀態(tài), 即使該線程正處于中斷狀態(tài), 在調(diào)用threadA.isInterrupted()會(huì)返回false

public class InterruptDemo {

    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> {
            System.out.println("threadA run");
        });
        threadA.start();
        //threadA中斷
        threadA.interrupt();
        //主線程等待threadA執(zhí)行結(jié)束
        threadA.join();
        System.out.println("after threadB interrupt threadA, threadA isInterrupted = " + threadA.isInterrupted());
    }
}

運(yùn)行結(jié)果

threadA run
after threadB interrupt threadA, threadA isInterrupted = false
2.4.5 Thread#interrupt源碼簡(jiǎn)介
public class Thread implements Runnable {   

    /** 
     * 判斷線程是否被中斷過(guò)
     * 不復(fù)位線程中斷標(biāo)記位
     */
    public boolean isInterrupted() {
        return isInterrupted(false);
    }
    
    /** 
     * 中斷線程, 并復(fù)位線程中斷標(biāo)記位
     * @return 線程是否被打斷
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
    
    /**
     * 中斷this線程 
     */
    public void interrupt() {
        //判斷this線程是否有權(quán)限訪問(wèn)Thread.currentThread
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
    
    private native boolean isInterrupted(boolean ClearInterrupted);
    
    public static native void sleep(long millis) throws InterruptedException;
}
2.4.5 Thread#interrupt使用場(chǎng)景

安全地終止線程, interrupt操作對(duì)進(jìn)行中的線程沒(méi)有影響, 但是會(huì)打斷線程的等待, 最合適用來(lái)取消或者停止任務(wù), 例如ThreadPoolExecutor#shutdown()就通過(guò)調(diào)用interrupt方法, 讓線程快速執(zhí)行完成, 最后再終止線程池中的任務(wù)

3. 線程間通信

每個(gè)線程擁有自己的椫褡幔空間, 相互配合的話就需要實(shí)現(xiàn)線程間通信

3.1 等待/通知機(jī)制

wait, notify, notifyAll都是Object類的final native方法

public class Object {

    /**
     * 在同步代碼塊中,持有當(dāng)前對(duì)象鎖的線程threadWait,
     * 放棄對(duì)象鎖,進(jìn)入waiting狀態(tài), 線程被放到等待隊(duì)列WaitQueue中
     * 
     * 直到其他持有對(duì)象鎖的線程threadNotify,執(zhí)行notify或notifyAll方法, 喚醒等待隊(duì)列中的一個(gè)或所有線程,
     * 線程從等待隊(duì)列WaitQueue中移到同步隊(duì)列SynchronizedQueue,
     * 線程從waiting狀態(tài)進(jìn)入blocked狀態(tài), 等到threadNotify執(zhí)行結(jié)束, 同步隊(duì)列中的線程開(kāi)始爭(zhēng)奪對(duì)象鎖,
     * 獲取到對(duì)象鎖之后, 獲取到鎖的線程會(huì)繼續(xù)執(zhí)行
     */
    public final void wait() throws InterruptedException {
        wait(0);
    }
    
    /**
     * 在同步代碼塊中,持有當(dāng)前對(duì)象鎖的線程threadWait,
     * 放棄對(duì)象鎖,進(jìn)入waiting狀態(tài), 線程被放到等待隊(duì)列WaitQueue中
     * 
     * 直到其他持有對(duì)象鎖的線程threadNotify,執(zhí)行notify或notifyAll方法,  喚醒等待隊(duì)列中的一個(gè)或所有線程,
     * 或者線程等待超時(shí)
     * 線程從等待隊(duì)列WaitQueue中移到同步隊(duì)列SynchronizedQueue,
     * 線程從waiting狀態(tài)進(jìn)入blocked狀態(tài), 等到threadNotify執(zhí)行結(jié)束, 同步隊(duì)列中的線程開(kāi)始爭(zhēng)奪對(duì)象鎖,
     * 獲取到對(duì)象鎖之后, 獲取到鎖的線程會(huì)繼續(xù)執(zhí)行
     */
    public final native void wait(long timeout) throws InterruptedException;
    
    /**
     * 在同步代碼塊中,持有當(dāng)前對(duì)象鎖的線程threadNotify,
     * 喚醒一個(gè)處于waiting狀態(tài)的線程threadWait
     *
     * 線程threadWait從等待隊(duì)列WaitQueue移到同步隊(duì)列SynchronizedQueue,
     * threadWait線程從waiting狀態(tài)進(jìn)入blocked狀態(tài)
     * 
     * 等到線程threadNotify同步代碼塊執(zhí)行結(jié)束, 放棄對(duì)象鎖
     * threadWait線程獲取到對(duì)象鎖, 開(kāi)始執(zhí)行同步代碼
     */
    public final native void notify();

    /**
     * 在同步代碼塊中,持有當(dāng)前對(duì)象鎖的線程threadNotify,
     * 喚醒所有處于waiting狀態(tài)的線程
     
     * 線程從等待隊(duì)列WaitQueue移到同步隊(duì)列SynchronizedQueue,
     * 線程從waiting狀態(tài)進(jìn)入blocked狀態(tài)
     * 
     * 等到線程threadNotify同步代碼塊執(zhí)行結(jié)束, 放棄對(duì)象鎖
     * 被喚醒的多個(gè)線程爭(zhēng)奪對(duì)象鎖, 獲取到對(duì)象鎖的線程, 開(kāi)始執(zhí)行同步代碼
     */
    public final native void notifyAll();
}
  1. 使用wait, notify和notifyAll方法時(shí)需要先對(duì)對(duì)象加鎖
  2. 調(diào)用wait之后, 線程由runnabe狀態(tài)轉(zhuǎn)為waiting狀態(tài), 并將線程放入等待隊(duì)列
  3. notify和notifyAll方法調(diào)用后, 等待線程依舊不會(huì)從wait()返回, 需要等待調(diào)用notify或notifyAll方法的線程釋放鎖之后, 等待線程才有機(jī)會(huì)從wait返回
  4. notify方法將等待隊(duì)列中的一個(gè)等待線程從等待隊(duì)列WaitQueue中移到同步隊(duì)列SynchronizedQueue中, notifyAll方法則是將等待隊(duì)列WaitQueue中的所有線程移到同步隊(duì)列SynchronizedQueue中, 被移動(dòng)的線程狀態(tài)有waiting狀態(tài)變?yōu)閎locked狀態(tài)
  5. 從wait方法返回的前期是獲得了調(diào)用對(duì)象的鎖
等待/通知經(jīng)典范式
  1. 等待方:
    1. 獲取對(duì)象鎖
    2. 如果條件不滿足, 那么調(diào)用對(duì)象的wait()方法, 被通知后仍要檢查條件
    3. 條件滿足則執(zhí)行對(duì)應(yīng)邏輯
    synchronized(object){
        while(條件不滿足){
            object.wait();
        }
        doSomething();
    }
    
  2. 通知方
    1. 獲取對(duì)象鎖
    2. 改變條件
    3. 通知等待線程
    synchronized(object){
        doSomething2();
        改變條件
        object.notifyAll();
    }
    
3.2 管道輸入流/輸出流

管道輸入/輸出流主要用于線程之間的數(shù)據(jù)傳輸

PipedOutputStream/PipedInputStream管道字節(jié)流, PipedReader/PipedWriter管道字符流

如下: 我們創(chuàng)建了一個(gè)輸入線程, 用來(lái)獲取控制臺(tái)的輸入的字符串, 保存到內(nèi)存中, 然后創(chuàng)建了一個(gè)打印線程, 用于獲取內(nèi)存中保存的控制臺(tái)字符串, 然后打印字符串

public class PipedExample {
    public static void main(String[] args) {
        //實(shí)例化輸入和打印線程
        Inputer inputer = new Inputer();
        Thread inputThread = new Thread(inputer);
        Thread printThread = new Thread(new Printer(inputer));
        //啟動(dòng)線程
        inputThread.start();
        printThread.start();
    }

    //打印線程
    private static class Printer implements Runnable {
        //PipedReader用于讀取流, 并打印出來(lái)
        private PipedReader in = new PipedReader();
        public Printer(Inputer inputer) {
            try {
                //PipedReader和PipedWriter必須先connect
                inputer.out.connect(in);
            } catch (IOException e) {}
        }

        @Override
        public void run() {
            int receive = 0;
            try {
                while ((receive = in.read()) != -1) {
                    //讀取內(nèi)存中的數(shù)據(jù), 并打印
                    System.out.print((char) receive);
                }
            } catch (IOException e) {}
        }
    }

    private static class Inputer implements Runnable {
        //輸出流
        private PipedWriter out = new PipedWriter();

        @Override
        public void run() {
            int receive = 0;
            try {
                while ((receive = System.in.read()) != -1) {
                    //讀取console的輸入,輸出到內(nèi)存中
                    out.write(receive);
                }
            } catch (IOException e) {}
        }
    }
}

在控制臺(tái)輸入 hello, world! 然后回車(chē), 運(yùn)行結(jié)果如下:

hello, world!
hello, world!
3.3 Thread.join

如果一個(gè)線程threadA執(zhí)行了threadB.join()語(yǔ)句, 那么threadA會(huì)等到線程threadB執(zhí)行中止之后, 才從threadB.join()返回, 繼續(xù)執(zhí)行threadA的代碼

下面的代碼通過(guò)join()方法, 讓10個(gè)線程按順序執(zhí)行

public class ThreadJoinExample {
    public static void main(String[] args) {
        Thread pre = new Thread(new OrderThread(null), "thread0");
        pre.start();
        for (int i = 1; i < 10; i++) {
            Thread thread = new Thread(new OrderThread(pre), "thread" + i);
            thread.start();
            pre = thread;
        }
    }
    // 按順序執(zhí)行的線程
    static class OrderThread implements Runnable {
        // 上一個(gè)線程
        private Thread pre;
        public OrderThread(Thread pre) {
            this.pre = pre;
        }
        @Override
        public void run() {
            if (pre == null) return;
            try {
                pre.join();
            } catch (InterruptedException e) {}
            System.out.println("after " + pre.getName() + " join, " 
            + Thread.currentThread().getName() + " terminate.");
        }
    }
}

運(yùn)行結(jié)果:

after thread0 join, thread1 terminate.
after thread1 join, thread2 terminate.
after thread2 join, thread3 terminate.
after thread3 join, thread4 terminate.
after thread4 join, thread5 terminate.
after thread5 join, thread6 terminate.
after thread6 join, thread7 terminate.
after thread7 join, thread8 terminate.
after thread8 join, thread9 terminate.

結(jié)果分析:
每個(gè)線程終止的前提就是前驅(qū)線程的終止, 每個(gè)線程等待前驅(qū)線程終止后, 才從join()方法返回

實(shí)現(xiàn)原理:
Thread類的join方法就是通過(guò)wait方法實(shí)現(xiàn)的

public final synchronized void join(long millis){
        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {}
    }

線程threadA調(diào)用join()方法, 相當(dāng)于threadA調(diào)用了wait()方法, 那么主線程threadMain就會(huì)進(jìn)入waiting狀態(tài), 等待threadA執(zhí)行結(jié)束, 調(diào)用自身的notifyAll()方法, 通知所有等待在threadA的線程

3.4 ThreadLocal

線程本地變量, 是一個(gè)以ThreadLocal為鍵, 任意對(duì)象為值的存儲(chǔ)結(jié)構(gòu), 線程可以通過(guò)ThreadLocal對(duì)象, 查詢或者設(shè)置綁定在這個(gè)線程上的值

ThreadLocal有一個(gè)內(nèi)部類ThreadLocalMap, ThreadLocalMap內(nèi)部有一個(gè)內(nèi)部類Entry, Entry以ThreadLocal為key, Object為value

ThreadLocal#get()方法執(zhí)行步驟

  1. 先獲取當(dāng)前線程
  2. 獲取當(dāng)前線程綁定的值(ThreadLocalMap)
    • 如果Map不為空, 那么獲取當(dāng)前ThreadLocal對(duì)應(yīng)的entry
    • 如果Map為空, 那么初始化值
  3. 設(shè)置線程綁定的值, 方法類似
//ThreadLocal源碼
public class ThreadLocal<T> {

    //獲取ThreadLocal中保存的元素
    public T get() {
        //獲取當(dāng)前線程
        Thread t = Thread.currentThread();
        //獲取線程對(duì)應(yīng)的Map
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //獲取當(dāng)前ThreadLocal對(duì)應(yīng)的entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                //獲取并返回值
                T result = (T)e.value;
                return result;
            }
        }
        //初始化并返回值
        return setInitialValue();
    }
    
    //初始化并返回值
    private T setInitialValue() {
        //初始化值, 默認(rèn)為null
        T value = initialValue();
        //獲取當(dāng)前線程
        Thread t = Thread.currentThread();
        //獲取當(dāng)前線程t的threadLocals
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            //如果Map為空,那么創(chuàng)建map
            createMap(t, value);
        return value;
    }
    
    void createMap(Thread t, T firstValue) {
        //實(shí)例化當(dāng)前線程的threadLocals
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    //ThreadLocalMap靜態(tài)內(nèi)部類
    static class ThreadLocalMap {
    
        //Entry靜態(tài)內(nèi)部類
        static class Entry extends WeakReference<ThreadLocal<?>> {
        
            //和ThreadLocal綁定的元素
            Object value;

            //ThreadLocal為key
            //任意Object為value
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    }
}

4. 線程池

服務(wù)端程序, 經(jīng)常面對(duì)的是客戶端傳入執(zhí)行時(shí)間短, 工作內(nèi)容較為單一的任務(wù), 需要服務(wù)端快速處理并返回結(jié)果

如果服務(wù)端每次接收到一個(gè)任務(wù), 就創(chuàng)建一個(gè)線程, 那么可能會(huì)創(chuàng)建很多線程, 耗盡CPU資源, 這會(huì)使操作系統(tǒng)頻繁的進(jìn)行上下文切換, 增加系統(tǒng)負(fù)載, 而且線程的創(chuàng)建和消亡都要耗費(fèi)系統(tǒng)資源

線程池很好的解決了這個(gè)問(wèn)題, 預(yù)先創(chuàng)建若干數(shù)量的線程, 并重復(fù)使用這些固定數(shù)目的線程來(lái)完成任務(wù)的執(zhí)行

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末筷转,一起剝皮案震驚了整個(gè)濱河市轻专,隨后出現(xiàn)的幾起案子鲜漩,更是在濱河造成了極大的恐慌祝懂,老刑警劉巖典予,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件螟蒸,死亡現(xiàn)場(chǎng)離奇詭異政溃,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)留储,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)翼抠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人欲鹏,你說(shuō)我怎么就攤上這事机久〕裟” “怎么了赔嚎?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)胧弛。 經(jīng)常有香客問(wèn)我尤误,道長(zhǎng),這世上最難降的妖魔是什么结缚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任损晤,我火速辦了婚禮,結(jié)果婚禮上红竭,老公的妹妹穿的比我還像新娘尤勋。我一直安慰自己喘落,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布最冰。 她就那樣靜靜地躺著瘦棋,像睡著了一般瓶蚂。 火紅的嫁衣襯著肌膚如雪撕捍。 梳的紋絲不亂的頭發(fā)上贱案,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天谅河,我揣著相機(jī)與錄音馆铁,去河邊找鬼球碉。 笑死肾砂,一個(gè)胖子當(dāng)著我的面吹牛盒音,可吹牛的內(nèi)容都是我干的达布。 我是一名探鬼主播团甲,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼黍聂!你這毒婦竟也來(lái)了伐庭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤分冈,失蹤者是張志新(化名)和其女友劉穎圾另,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體雕沉,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡集乔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坡椒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扰路。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖倔叼,靈堂內(nèi)的尸體忽然破棺而出汗唱,到底是詐尸還是另有隱情,我是刑警寧澤丈攒,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布哩罪,位于F島的核電站,受9級(jí)特大地震影響巡验,放射性物質(zhì)發(fā)生泄漏际插。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一显设、第九天 我趴在偏房一處隱蔽的房頂上張望框弛。 院中可真熱鬧,春花似錦捕捂、人聲如沸瑟枫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)慷妙。三九已至榜旦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間景殷,已是汗流浹背溅呢。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猿挚,地道東北人咐旧。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像绩蜻,于是被迫代替她去往敵國(guó)和親铣墨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容