線程基礎猜谚、線程之間的共享和協(xié)作

基礎概念

CPU核心數和線程數的比例:1:1,超線程技術比例:1:2
CPU時間片輪轉機制(RR調度)辨嗽,涉及線程上下文切換
進程和線程
  • 進程:程序運行資源分配的最小單位世落,一個進程內部有多個線程,共享這個進程的資源糟需,進程可獨立運行屉佳,進程之間相互獨立
  • 線程:CPU調度的最小單位,線程本身不能獨立運行洲押,必須依附于進程武花,自己不能申請資源,只能共享使用進程的資源
并行和并發(fā)
  • 并行:同一時刻可以處理事情的能力
  • 并發(fā):與單位時間相關杈帐,一定單位時間內處理事情的能力
  1. 舉例1:食堂有8個窗口可同時打飯体箕,則食堂打飯的并行數為8,一分鐘內一個窗口可以為3個學生服務挑童,則一分鐘內食堂都并發(fā)數為 8 * 3 = 24
  2. 舉例2:CPU的核心數為8累铅,則CPU的并發(fā)數為8,CPU的時間片分隔執(zhí)行周期為100ms炮沐,則1秒內CPU并發(fā)數為 1000 / 100 * 8 = 80
高并發(fā)的意義
  • 充分利用CPU資源
  • 加快用戶響應時間
  • 使代碼模塊化争群,異步化處理
高并發(fā)的注意事項
  • 資源共享,會導致線程沖突大年,照成線程死鎖
  • 創(chuàng)建多過的線程,會導致資源枯竭

Java線程

Java啟動線程的方式
  • 繼承Thread類
  • 實現(xiàn)Runnable接口玉雾,無返回值
  • 實現(xiàn)Callable接口翔试,有返回值
線程結束
  • 程序執(zhí)行完成,自然結束
  • 拋出異常結束
  • stop()复旬、resume()垦缅、pause(),暴力停止驹碍,線程資源無法釋放壁涎,不建議使用
  • interrupt()凡恍、interrupted()、isInterrupted()三種java線程協(xié)作方式停止線程
  • thread.interrupt():中斷當前thread線程怔球,并不是強制關閉線程嚼酝,而且將中斷標記位設置為true,線程在某一個時刻中斷竟坛,無法控制
  • thread.isInterrupted():僅返回thread線程中斷狀態(tài)
  • static interrupted():靜態(tài)方法闽巩,作用于當前所在線程,返回當前線程的中斷狀態(tài)担汤,并會將中斷標記位設置為false
  • 線程拋出InterruptedException異常時涎跨,會重置中斷標記位為false,需再次調用interrupt()方法才可退出循環(huán)

參考:http://www.reibang.com/p/4da928eed5e8

線程的生命周期
未命名文件2.jpg
線程的優(yōu)先級:myThread.setPriority(6); // 1 - 10之間崭歧,不一定生效
守護線程:如果主線程結束隅很,則守護線程強制結束

myThread.setDaemon(true)

注:守護線程會導致線程內的finally()方法不一定會執(zhí)行,導致資源無法釋放

sleep,wait,yield,join的區(qū)別

  • sleep():在指定時間內讓正在運行的線程暫停率碾,使線程進入暫停狀態(tài)叔营,sleep()并不會釋放鎖
  • wait():在其他線程調用notify()或notifyAll()方法之前,線程阻塞播掷,wait()方法會釋放鎖审编,這樣別的線程有機會競爭到鎖,wait()歧匈、notify()垒酬、notifyAll()必須在synchronized方法塊或者修飾的方法上使用
  • yield():暫停正在執(zhí)行的線程,yield()方法只是將線程的狀態(tài)由運行狀態(tài)變成就緒狀態(tài)件炉,可能會在一個執(zhí)行周期搶占到了資源勘究,由就緒狀態(tài)變成運行狀態(tài)重新執(zhí)行,所以yield()方法調用后線程只需要重新獲取CPU的機會斟冕,sleep()方法得等到固定時間結束后才會進入就緒狀態(tài)
  • join():等待調用join()方法的實例所代表的線程執(zhí)行完成后口糕,當前線程才能繼續(xù)執(zhí)行,比如main線程中調用了t.join()磕蛇,則主線程需要等待t線程結束后才會繼續(xù)執(zhí)行

線程共享

synchronized:內置鎖
  • 對象鎖:鎖的范圍是對象
  • 類鎖:鎖的范圍是類對應的class對象
volatile:最輕量級的同步機制景描,強制線程從主內存獲取值,可以保障線程的可見性秀撇,但無法保障線程的原子性超棺,volatile是非線程安全的
ThreadLocal:空間換線程安全,每個線程保存了對象的副本呵燕,對象之間相互獨立
wait()棠绘、notify()、notifyAll()方法

wait():調用wait()時,當前線程進入阻塞狀態(tài)氧苍,并釋放鎖夜矗,等待別的線程調用notify()或notifyAll()方法喚醒
notify():調用notify()方法通知喚醒某一個wait()方法所阻塞等待的線程
notifyAll(): 通知喚醒所有因調wait()方法所阻塞等待的線程

等待通知機制

等待方:
1、獲取對象鎖
2让虐、循環(huán)判斷是否滿足處理的條件紊撕,不滿足調用wait()方法
3、滿足條件執(zhí)行業(yè)務邏輯
通知方:
1澄干、獲取對象鎖
2逛揩、改變條件
3、通知所有等待在鎖對象上的線程

實例1:wait()麸俘、notifyAll()實現(xiàn)一個快遞通知辩稽,當快遞的里程數>100公里或者城市發(fā)生變化時給用戶通知

/**
 * 當快遞的公里數大于100公里或城市不在北京時,給客戶發(fā)出提醒消息
 */
public class Express {

    public static final String CURR_CITY = "北京";

    private Object object = new Object();

    /**
     * 快遞行走的公里數
     */
    private int km;

    /**
     * 快遞當前位置
     */
    private String site = "北京";

    /**
     * 修改快遞公里數
     * @param km
     */
    public void changeKm(int km) {
        synchronized (object) {
            System.out.println(Thread.currentThread().getName() + " km : " + km);
            this.km = km;
            object.notifyAll();
        }
    }

    /**
     * 修改快遞城市
     * @param city
     */
    public void changeSite(String city) {
        synchronized (object) {
            System.out.println(Thread.currentThread().getName() + " site : " + site);
            this.site = city;
            object.notifyAll();
        }
    }

    /**
     * 等待公里數變化
     */
    public void waitKm() throws InterruptedException {
        synchronized (object) {
            while (this.km <= 100) {
                object.wait();
                System.out.println(Thread.currentThread().getName() + " check km...");
            }
            System.out.println(Thread.currentThread().getName() + " , send change, curr km : " + km);
        }
    }

    /**
     * 等待城市變化
     */
    public void waitSite() throws InterruptedException {
        synchronized (object) {
            while (CURR_CITY.equals(site)) {
                object.wait();
                System.out.println(Thread.currentThread().getName() + " check site...");
            }
            System.out.println(Thread.currentThread().getName() + " send change, curr site : " + site);
        }
    }
}

完整代碼: https://github.com/shawntime/test-enjoy-architect/tree/master/test-concurrency/src/main/java/com/shawntime/enjoy/architect/concurrency/waitandnotify/express

實例2:實現(xiàn)一個自定義的數據庫連接池
/**
 * 自定義實現(xiàn)線程池
 */
public class DBPool {

    private LinkedList<MyConnection> DB_POOL = new LinkedList<>();

    private static final int INITIALIZE_SIZE = 10;

    private Object lock = new Object();

    public DBPool(int initializeSize) {
        int size = initializeSize;
        if (initializeSize <= 0) {
            size = INITIALIZE_SIZE;
        }
        for (int i = 0; i < size; ++i) {
            MyConnection connection = new MyConnection();
            DB_POOL.add(connection);
        }
    }

    /**
     * 獲取鏈接
     */
    public MyConnection getConnection(long waitTime) throws InterruptedException {
        synchronized (lock) {
            if (waitTime <= 0) {
                while (DB_POOL.isEmpty()) {
                    lock.wait();
                }
                return DB_POOL.removeFirst();
            } else {
                long endTimeStamp = System.currentTimeMillis() + waitTime;
                long remainTime = waitTime;
                while (DB_POOL.isEmpty() && remainTime > 0) {
                    lock.wait();
                    remainTime = endTimeStamp - System.currentTimeMillis();
                }
                if (DB_POOL.isEmpty()) {
                    return null;
                }
                return DB_POOL.removeFirst();
            }
        }
    }

    /**
     * 釋放連接
     */
    public void releaseConnection(MyConnection connection) {
        if (connection == null) {
            return;
        }
        synchronized (lock) {
            DB_POOL.addLast(connection);
            lock.notifyAll();
        }
    }
}

完整代碼見: https://github.com/shawntime/test-enjoy-architect/tree/master/test-concurrency/src/main/java/com/shawntime/enjoy/architect/concurrency/waitandnotify/pool

yield()从媚、wait()逞泄、notify()/notifyAll()、sleep()方法對鎖的影響

  • yield():暫停正在執(zhí)行的線程拜效,線程由進行狀態(tài)轉換為就緒狀態(tài)喷众,但不會釋放鎖
  • sleep():暫停正在執(zhí)行的線程,線程由進行狀態(tài)轉換為暫停狀態(tài)紧憾,也不會釋放鎖
  • wait():阻塞當前線程到千,調用wait()方法之前,必須先獲取到鎖赴穗,調用wait()方法之后會釋放鎖憔四,當wait()方法返回時又會重新獲取鎖
  • notify()、notifyAll():調用notify()方法之前必須持有鎖般眉,喚醒wait()方法阻塞的線程了赵,其本身方法并不會釋放鎖,而是要等待調用notify()的方法結束后所持有的鎖釋放甸赃,所以notify()一般都放在方法的最后調用

join()方法

線程A調用線程B的join()方法柿汛,則線程A要等到線程B執(zhí)行結束后,線程A才能繼續(xù)執(zhí)行

import com.shawntime.enjoy.architect.concurrency.SleepUtils;

/**
 * join()方法測試
 */
public class JoinTest {

    private static class MyThread extends Thread {

        private Thread thread;

        public MyThread(String name, Thread thread) {
            super(name);
            this.thread = thread;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " waiting for " + thread.getName());
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "執(zhí)行完成...");
        }
    }

    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        for (int i = 0; i < 10; ++i) {
            MyThread myThread = new MyThread("my-thread" + i, thread);
            myThread.start();
            thread = myThread;
        }

        for (int i = 0; i < 3; ++i) {
            System.out.println("主線程睡眠" + (i + 1) + "秒");
            SleepUtils.sleepBySeconds(1);
        }

        System.out.println("Main方法執(zhí)行完成...");
    }
}

執(zhí)行結果:
Main方法執(zhí)行完成...
my-thread0執(zhí)行完成...
my-thread1執(zhí)行完成...
my-thread2執(zhí)行完成...
my-thread3執(zhí)行完成...
my-thread4執(zhí)行完成...
my-thread5執(zhí)行完成...
my-thread6執(zhí)行完成...
my-thread7執(zhí)行完成...
my-thread8執(zhí)行完成...
my-thread9執(zhí)行完成...

join()可以讓多線程串行執(zhí)行
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末埠对,一起剝皮案震驚了整個濱河市络断,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌项玛,老刑警劉巖妓羊,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異稍计,居然都是意外死亡,警方通過查閱死者的電腦和手機裕循,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門臣嚣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來净刮,“玉大人,你說我怎么就攤上這事硅则⊙透福” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵怎虫,是天一觀的道長暑认。 經常有香客問我,道長大审,這世上最難降的妖魔是什么蘸际? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮徒扶,結果婚禮上粮彤,老公的妹妹穿的比我還像新娘。我一直安慰自己姜骡,他們只是感情好导坟,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著圈澈,像睡著了一般惫周。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上康栈,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天递递,我揣著相機與錄音,去河邊找鬼谅将。 笑死漾狼,一個胖子當著我的面吹牛,可吹牛的內容都是我干的饥臂。 我是一名探鬼主播逊躁,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼隅熙!你這毒婦竟也來了稽煤?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤囚戚,失蹤者是張志新(化名)和其女友劉穎酵熙,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體驰坊,經...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡匾二,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片察藐。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡皮璧,死狀恐怖,靈堂內的尸體忽然破棺而出分飞,到底是詐尸還是另有隱情悴务,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布譬猫,位于F島的核電站讯檐,受9級特大地震影響,放射性物質發(fā)生泄漏染服。R本人自食惡果不足惜别洪,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肌索。 院中可真熱鬧蕉拢,春花似錦、人聲如沸诚亚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽站宗。三九已至闸准,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間梢灭,已是汗流浹背夷家。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留敏释,地道東北人库快。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像钥顽,于是被迫代替她去往敵國和親义屏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內容