線程yield,start,run,join,sleep 傻傻分不清楚

線程:cpu調度的最小單位
進程:操作系統分配資源的最小單位(線程共享進程內部的資源)

java 中提供了Thread 類,而這個類有幾個方法

yield,start,run(來自接口runnable),sleep,

yield: 欲罷能否?

告訴調度器自己當前可以讓出資源(但是不一定有效)

 A hint to the scheduler that the current thread is willing to yield
     * its current use of a processor. The scheduler is free to ignore this
     * hint.

測試代碼:

(測試效果基本看不出)

    Thread[] threads = new Thread[10];
        threads[0] = new Thread(() -> {
            // ReadThreadLocal readThreadLocal2 = new ReadThreadLocal();
            //readThreadLocal2.begin();
            try {
                Thread.currentThread().sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        });
        threads[0].setName("-1");
        threads[0].start();
        for (int i = 1; i < 10; i++) {
            final Thread pre = threads[i - 1];
            final int index = i;
            threads[i] = new Thread(() -> {
                try {
                    // 這裏嘗試1,2,號線程讓出cpu
                    if (index < 3) {
                        Thread.yield();
                    }
                    // 以下邏輯 可以保證  6,7,8,9 線程是有序執行
                    else if (index > 6) {
                        pre.join();
                    }
                    //   ReadThreadLocal readThreadLocal2 = new ReadThreadLocal();
                    //   readThreadLocal2.begin();
                    Thread.sleep(500);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            });
            threads[i].setName(i + "");
            threads[i].start();

        }

效果:(這三次輸出只是看到了join 接力棒的效果,始終保證6,7,8,9之間是嚴格有序的)
第一次:

-1 1 3 2 5 4 6 7 8 9

第二次

-1 1 6 5 2 4 3 7 8 9

第三次

-1 3 1 4 2 6 5 7 8 9

start:線程啓動入口

啓動線程(線程進入就緒態:(也就是等待被cpu調度,並不是說先調用start,就先執行run方法)
run: 線程真正的執行體,線程需要處理的業務邏輯全在這裏

join:接力棒

join(othrerThread)

Waits for this thread to die// 等待到參數中的thread 死亡,自己纔開始

(可以類比爲線程進行接力賽,下一棒需要等上一棒到了才能開始跑),也就是可以達到有序執行的目的

代碼測試

看例子程序:

Thread[] threads = new Thread[10];
        threads[0] = new Thread(() -> {
            
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() );
        });
        threads[0].setName("-1");
        threads[0].start();
        for (int i = 1; i < 10; i++) {
            final Thread pre = threads[i - 1];
            threads[i] = new Thread(() -> {
                try {
                    pre.join();
                   
                   
                    Thread.sleep(500);
                    System.out.println(Thread.currentThread().getName() );
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            });
            threads[i].setName(i + "");
            threads[i].start();

        }

在這裏插入圖片描述

sleep: 抱鎖入睡

注意不會丟失monitors,監視器,也就是不會釋放鎖

Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.

代碼測試:

線程1,線程2 都去獲取同一個對象的monitor,各自都在獲取到之後輸出 “sleep start”+id;sleep(500);輸出"sleep end "+id
如果不釋放鎖的話,要麼是線程1 連續輸出兩條,然後線程2連續輸出兩條
否則是反過來
代碼使用10個線程


public class TestSleep {


    public static void main(String[] args) {
        final Object lock=new Object();
        for (int i=0;i<10;i++) {
            Thread t=new Thread(()->{
                synchronized (lock){

                    try {
                        System.out.println("sleep start"+Thread.currentThread().getName());
                        Thread.sleep(500);
                        System.out.println("sleep end"+Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.setName(i+"");
            t.start();
        }
    }

}

效果:

在這裏插入圖片描述

Object 的方法:wait VS notify

wait:等待,會釋放鎖,將當前位置保存,暫停執行
norify:喚醒,會釋放鎖,但是會將synchronized修飾代碼塊執行完之後纔會釋放

僞代碼描述

以下列僞代碼爲例:

 第一塊代碼塊
 synchonized(helloObject){// 這裏相當於是嘗試獲取對象helloObject 的鎖
   輸出:我是hello1
   helloObject.wait();
   輸出:我是hello2
}

第二塊代碼塊
synchronized (helloObject){
輸出:我是word1:
helloObject.notifyAll();
輸出:我是word2

}
  1. 假設上述代碼:線程1進入到第一塊代碼塊,此時線程2阻塞到第二行代碼塊的synchronized 處,線程2沒有獲取到鎖
  2. 線程1調用helloObject.wait();掛起線程,進入waiting 狀態;// 已經輸出 hello1
  3. 線程2獲取到helloObject 的鎖, //輸出我是word1
  4. 線程2調用helloObject.notifyAll() //喚醒所有處於該對象的waiting 狀態的線程
  5. 線程2退出synchronized 同步塊;//已輸出 我是word2
  6. 線程2釋放helloObject 的monitor(看成鎖)
  7. 線程1繼續// 輸出我是hello2
    以下附上java併發編程藝術中的一個例子:等待-喚醒機制
/**
 * @author wangwei
 * @date 2019/3/10 9:15
 * @classDescription 場景:一個線程修改了一個對象的值,而另一個線程感覺到了變化,然後進行相應的操作
 * 整個過程開始於一個線程,作用於另一個線程`,前者是生產者,後者是消費者
 * <p>
 * 這種生產者消費者模式隔離了"做什麼" 和"怎麼做"
 * java 中的實現方式
 * while(value!=desire){
 * Thread.sleep(1000);
 * }
 * doSomething();
 * <p>
 * 上述代碼:在條件不滿足時,sleep一段時間,避免過快的"無效嘗試"
 * <p>
 * 存在的問題:
 * 1.難以確保及時性:睡眠時基本不佔用處理器資源,但是如果睡眠過久,難以及時發現條件已經變化
 * 2.難以降低開銷:如果降低睡眠時間,消費者能及時發現條件變化,但是,卻可能需要浪費更多的資源
 * <p>
 * 兩點矛盾,但是java內置了等待/通知機制,可以解決這個問題
 */
public class WaitNotify {
    static boolean flag = true;
    static Object lock = new Object();


    public static void main(String[] args) throws InterruptedException {
        Thread waitThread = new Thread(new Wait(), "WaitThread");
        waitThread.start();
        TimeUnit.SECONDS.sleep(1);
        Thread notifyThread = new Thread(new Notify(), "NotifyThread");
        notifyThread.start();
    }

    static class Wait implements Runnable {

        @Override
        public void run() {
            synchronized (lock) {
                // 當條件不滿足時,繼續wait,但是會釋放鎖lock
                while (flag) {
                    try {
                        System.out.println(Thread.currentThread() + " flag is true,wait @ "
                                + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //條件滿足時,完成工作
                System.out.println(Thread.currentThread() + " flag is false,running@ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));

            }

        }
    }


    static class Notify implements Runnable {

        @Override
        public void run() {
            //加鎖,擁有lock 的monitor
            synchronized (lock) {
                // 獲取lock鎖,然後進行通知(不會釋放鎖)
                // 直到當前線程釋放lock鎖之後,WaitThread 才能在wait方法上返回
                System.out.println(Thread.currentThread() + " hold lock ,notify @ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                lock.notifyAll();
                flag = false;
                SleepUtil.second(5);

            }
            // 再次加鎖
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock again ,sleep @ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                SleepUtil.second(5);
            }
        }
    }
}


輸出:
在這裏插入圖片描述

等待喚醒機制應用–線程池

奉上併發編程藝術一書中簡易web服務器
可前往github 獲取
https://github.com/TopForethought/java/tree/master/src/top/forethought/concurrency/threads/httpserver
這裏就不再粘貼代碼了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章