找不到libmmd.dll无法继续执行代码_300 行代码带你秒懂 Java 多线程!| 原力计划...
作者 |?永遠在路上【】
責編 | 胡巍巍
出品 | CSDN博客
線程線程的概念,百度是這樣解釋的:線程(英語:Thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發多個線程,每條線程并行執行不同的任務。在Unix System V及SunOS中也被稱為輕量進程(Lightweight Processes),但輕量進程更多指內核線程(Kernel Thread),而把用戶線程(User Thread)稱為線程。1.1 線程與進程的區別進程:指在系統中正在運行的一個應用程序;程序一旦運行就是進程;進程——資源分配的最小單位。線程:系統分配處理器時間資源的基本單元,或者說進程之內獨立執行的一個單元執行流。線程——程序執行的最小單位。也就是,進程可以包含多個線程,而線程是程序執行的最小單位。1.2 線程的狀態- NEW:線程剛創建
- RUNNABLE: 在JVM中正在運行的線程,其中運行狀態可以有運行中RUNNING和READY兩種狀態,由系統調度進行狀態改變。
- BLOCKED:線程處于阻塞狀態,等待監視鎖,可以重新進行同步代碼塊中執行
- WAITING : 等待狀態
- TIMED_WAITING: 調用sleep() join() wait()方法可能導致線程處于等待狀態
- TERMINATED: 線程執行完畢,已經退出
- 它們最大本質的區別是,Sleep()不釋放同步鎖,Wait()釋放同步鎖。
- 還有用法的上的不同是:Sleep(milliseconds)可以用時間指定來使他自動醒過來,如果時間不到你只能調用Interreput()來強行打斷;Wait()可以用Notify()直接喚起。
- 這兩個方法來自不同的類分別是Thread和Object
- 最主要是Sleep方法沒有釋放鎖,而Wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。
- 相同 :Sleep()和yield()都會釋放CPU。
- 不同:Sleep()使當前線程進入停滯狀態,所以執行Sleep()的線程在指定的時間內肯定不會執行;yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態后馬上又被執行。Sleep()可使優先級低的線程得到執行的機會,當然也可以讓同優先級和高優先級的線程有執行的機會;yield()只能使同優先級的線程有執行的機會。
- 互斥條件:顧名思義,線程對資源的訪問是排他性,當該線程釋放資源后下一線程才可進行占用。
- 請求和保持:簡單來說就是自己拿的不放手又等待新的資源到手。線程T1至少已經保持了一個資源R1占用,但又提出對另一個資源R2請求,而此時,資源R2被其他線程T2占用,于是該線程T1也必須等待,但又對自己保持的資源R1不釋放。
- 不可剝奪:在沒有使用完資源時,其他線性不能進行剝奪。
- 循環等待:一直等待對方線程釋放資源。
- 原子性:Atomic包、CAS算法、Synchronized、Lock。
- 可見性:Synchronized、Volatile(不能保證原子性)。
- 有序性:Happens-before規則。
- 互斥同步:Synchronized、Lock。
- 非阻塞同步:CAS。
- 無需同步的方案:如果一個方法本來就不涉及共享數據,那它自然就無需任何同步操作去保證正確性。
- Synchronized關鍵字
- Lock
- CAS、原子變量
- ThreadLocl:簡單來說就是讓每個線程,對同一個變量,都有自己的獨有副本,每個線程實際訪問的對象都是自己的,自然也就不存在線程安全問題了。
- Volatile
- CopyOnWrite寫時復制
????}
}class?MyThread?extends?Thread?{@Overridepublic?void?run()?{
????????System.out.println(Thread.currentThread().getName()?+?"\t"?+?Thread.currentThread().getId());
????}
}實現Runable接口:public?class?RunableCreateTest?{public?static?void?main(String[]?args)?{
????????MyRunnable?runnable?=?new?MyRunnable();new?Thread(runnable).start();
????}
}class?MyRunnable?implements?Runnable?{@Overridepublic?void?run()?{
????????System.out.println(Thread.currentThread().getName()?+?"\t"?+?Thread.currentThread().getId());
????}
}通過Callable和Future創建線程:public?class?CallableCreateTest?{public?static?void?main(String[]?args)?throws?Exception?{//?將Callable包裝成FutureTask,FutureTask也是一種Runnable
????????MyCallable?callable?=?new?MyCallable();
????????FutureTask?futureTask?=?new?FutureTask<>(callable);new?Thread(futureTask).start();//?get方法會阻塞調用的線程
????????Integer?sum?=?futureTask.get();
????????System.out.println(Thread.currentThread().getName()?+?Thread.currentThread().getId()?+?"="?+?sum);
????}
}class?MyCallable?implements?Callable<Integer>?{@Overridepublic?Integer?call()?throws?Exception?{
????????System.out.println(Thread.currentThread().getName()?+?"\t"?+?Thread.currentThread().getId()?+?"\t"?+?new?Date()?+?"?\tstarting...");int?sum?=?0;for?(int?i?=?0;?i?<=?100000;?i++)?{
????????????sum?+=?i;
????????}
????????Thread.sleep(5000);
????????System.out.println(Thread.currentThread().getName()?+?"\t"?+?Thread.currentThread().getId()?+?"\t"?+?new?Date()?+?"?\tover...");return?sum;
????}
}線程池方式創建:實現Runnable接口這種方式更受歡迎,因為這不需要繼承Thread類。在應用設計中已經繼承了別的對象的情況下,這需要多繼承(而Java不支持多繼承,但可以多實現啊),只能實現接口。同時,線程池也是非常高效的,很容易實現和使用。實際開發中,阿里巴巴開發插件一直提倡使用線程池創建線程,原因在下方會解釋,所以上面的代碼我就只簡寫了一些Demo。2.1 線程池創建線程線程池,顧名思義,線程存放的地方。和數據庫連接池一樣,存在的目的就是為了較少系統開銷,主要由以下幾個特點:降低資源消耗。通過重復利用已創建的線程降低線程創建和銷毀造成的消耗(主要)。提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。提高線程的可管理性。線程是稀缺資源,如果無限制地創建,不僅會消耗系統資源,還會降低系統的穩定性。Java提供四種線程池創建方式:
- newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
- newFixedThreadPool創建一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。
- newScheduledThreadPool創建一個定長線程池,支持定時及周期性任務執行。
- newSingleThreadExecutor創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
??????????????????????????????TimeUnit?unit,
??????????????????????????????BlockingQueue?workQueue,
??????????????????????????????ThreadFactory?threadFactory,
??????????????????????????????RejectedExecutionHandler?handler)?{if?(corePoolSize?0?||
????????????maximumPoolSize?<=?0?||
????????????maximumPoolSize?????????????keepAliveTime?0)throw?new?IllegalArgumentException();if?(workQueue?==?null?||?threadFactory?==?null?||?handler?==?null)throw?new?NullPointerException();this.corePoolSize?=?corePoolSize;this.maximumPoolSize?=?maximumPoolSize;this.workQueue?=?workQueue;this.keepAliveTime?=?unit.toNanos(keepAliveTime);this.threadFactory?=?threadFactory;this.handler?=?handler;
????}參數說明如下:
- corePoolSize:線程池的核心線程數,即便線程池里沒有任何任務,也會有corePoolSize個線程在候著等任務。
- maximumPoolSize:最大線程數,不管提交多少任務,線程池里最多工作線程數就是maximumPoolSize。
- keepAliveTime:線程的存活時間。當線程池里的線程數大于corePoolSize時,如果等了keepAliveTime時長還沒有任務可執行,則線程退出。
- Unit:這個用來指定keepAliveTime的單位,比如秒:TimeUnit.SECONDS。
- BlockingQueue:一個阻塞隊列,提交的任務將會被放到這個隊列里。
- threadFactory:線程工廠,用來創建線程,主要是為了給線程起名字,默認工廠的線程名字:pool-1-thread-3。
- handler:拒絕策略,當線程池里線程被耗盡,且隊列也滿了的時候會調用。
????????????ArrayBlockingQueue?queue?=?new?ArrayBlockingQueue(maxSize);new?Thread(new?Productor(queue)).start();new?Thread(new?Customer(queue)).start();
????????}
????}class?Customer?implements?Runnable?{private?BlockingQueue?queue;
????????Customer(BlockingQueue?queue)?{this.queue?=?queue;
????????}
????????@Overridepublic?void?run()?{this.cusume();
????????}private?void?cusume()?{while?(true)?{try?{int?count?=?(int)?queue.take();
????????????????????System.out.println("customer正在消費第"?+?count?+?"個商品===");//?只是為了方便觀察輸出結果
????????????????????Thread.sleep(10);
????????????????}?catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????}
????????}
????}class?Productor?implements?Runnable?{private?BlockingQueue?queue;private?int?count?=?1;
????????Productor(BlockingQueue?queue)?{this.queue?=?queue;
????????}
????????@Overridepublic?void?run()?{this.product();
????????}private?void?product()?{while?(true)?{try?{queue.put(count);
????????????????????System.out.println("生產者正在生產第"?+?count?+?"個商品");
????????????????????count++;
????????????????}?catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????}
????????}
????}//輸出如下/**
生產者正在生產第1個商品
生產者正在生產第2個商品
生產者正在生產第3個商品
生產者正在生產第4個商品
生產者正在生產第5個商品
customer正在消費第1個商品===
*/2.2.3 LinkedBlockingQueue基于鏈表的阻塞隊列,內部也維護了一個數據緩沖隊列。需要我們注意的是如果構造一個LinkedBlockingQueue對象,而沒有指定其容量大小。LinkedBlockingQueue會默認一個類似無限大小的容量(Integer.MAX_VALUE),這樣的話,如果生產者的速度一旦大于消費者的速度,也許還沒有等到隊列滿阻塞產生,系統內存就有可能已被消耗殆盡了。2.2.4 LinkedBlockingQueue和ArrayBlockingQueue的主要區別
- ArrayBlockingQueue的初始化必須傳入隊列大小,LinkedBlockingQueue則可以不傳入。
- ArrayBlockingQueue用一把鎖控制并發,LinkedBlockingQueue倆把鎖控制并發,鎖的細粒度更細。即前者生產者消費者進出都是一把鎖,后者生產者生產進入是一把鎖,消費者消費是另一把鎖。
- ArrayBlockingQueue采用數組的方式存取,LinkedBlockingQueue用Node鏈表方式存取。
- AbortPolicy:不處理,直接拋出異常。
- CallerRunsPolicy:只用調用者所在線程來運行任務,即提交任務的線程。
- DiscardOldestPolicy:LRU策略,丟棄隊列里最近最久不使用的一個任務,并執行當前任務。
- DiscardPolicy:不處理,丟棄掉,不拋出異常。
RUNNING:在這個狀態的線程池能判斷接受新提交的任務,并且也能處理阻塞隊列中的任務。
SHUTDOWN:處于關閉的狀態,該線程池不能接受新提交的任務,但是可以處理阻塞隊列中已經保存的任務,在線程處于RUNNING狀態,調用shutdown()方法能切換為該狀態。
STOP:線程池處于該狀態時既不能接受新的任務也不能處理阻塞隊列中的任務,并且能中斷現在線程中的任務。當線程處于RUNNING和SHUTDOWN狀態,調用shutdownNow()方法就可以使線程變為該狀態。
TIDYING:在SHUTDOWN狀態下阻塞隊列為空,且線程中的工作線程數量為0就會進入該狀態,當在STOP狀態下時,只要線程中的工作線程數量為0就會進入該狀態。
TERMINATED:在TIDYING狀態下調用terminated()方法就會進入該狀態。可以認為該狀態是最終的終止狀態。
回到線程池創建ThreadPoolExecutor,我們了解了這些參數,再來看看ThreadPoolExecutor的內部工作原理:- 判斷核心線程是否已滿,是進入隊列,否:創建線程
- 判斷等待隊列是否已滿,是:查看線程池是否已滿,否:進入等待隊列
- 查看線程池是否已滿,是:拒絕,否創建線程
????????????c?=?ctl.get();
????????}//如果不小于corePoolSize,則將任務添加到workQueue隊列。if?(isRunning(c)?&&?workQueue.offer(command))?{int?recheck?=?ctl.get();if?(!?isRunning(recheck)?&&?remove(command))
????????????????reject(command);else?if?(workerCountOf(recheck)?==?0)
????????????????addWorker(null,?false);
????????}//如果放入workQueue失敗,則創建線程執行任務,如果這時創建線程失敗(當前線程數不小于maximumPoolSize時),就會調用reject(內部調用handler)拒絕接受任務。else?if?(!addWorker(command,?false))
????????????reject(command);
????}AddWorker方法:
- 創建Worker對象,同時也會實例化一個Thread對象。在創建Worker時會調用threadFactory來創建一個線程。
- 然后啟動這個線程。
- runState:即rs 表明當前線程池的狀態,是否處于Running,Shutdown,Stop,Tidying。
- workerCount:即wc表明當前有效的線程數。
31~29位那低28位呢,就是記錄當前線程的總線數啦:????//?Packing?and?unpacking?ctlprivate?static?int?runStateOf(int?c)?????{?return?c?&?~CAPACITY;?}private?static?int?workerCountOf(int?c)??{?return?c?&?CAPACITY;?}private?static?int?ctlOf(int?rs,?int?wc)?{?return?rs?|?wc;?}從上述代碼可以看到workerCountOf這個函數傳入ctl之后,是通過CTL&CAPACITY操作來獲取當前運行線程總數的。也就是RunningState|WorkCount&CAPACITY,算出來的就是低28位的值。因為CAPACITY得到的就是高3位(29-31位)位0,低28位(0-28位)都是1,所以得到的就是ctl中低28位的值。而runStateOf這個方法的話,算的就是RunningState|WorkCount&CAPACITY,高3位的值,因為CAPACITY是CAPACITY的取反,所以得到的就是高3位(29-31位)為1,低28位(0-28位)為0,所以通過&運算后,所得到的值就是高3為的值。簡單來說就是ctl中是高3位作為狀態值,低28位作為線程總數值來進行存儲。2.3.2 shutdownNow和shutdown的區別看源碼發現有兩種近乎一樣的方法,shutdownNow和shutdown,設計者這么設計自然是有它的道理,那么這兩個方法的區別在哪呢?
- shutdown會把線程池的狀態改為SHUTDOWN,而shutdownNow把當前線程池狀態改為STOP。
- shutdown只會中斷所有空閑的線程,而shutdownNow會中斷所有的線程。
- shutdown返回方法為空,會將當前任務隊列中的所有任務執行完畢;而shutdownNow把任務隊列中的所有任務都取出來返回。
????????Thread?wt?=?Thread.currentThread();
????????Runnable?task?=?w.firstTask;
????????w.firstTask?=?null;
????????w.unlock();?//?allow?interrupts
????????boolean?completedAbruptly?=?true;try?{while?(task?!=?null?||?(task?=?getTask())?!=?null)?{
????????????????w.lock();//?If?pool?is?stopping,?ensure?thread?is?interrupted;//?if?not,?ensure?thread?is?not?interrupted.??This//?requires?a?recheck?in?second?case?to?deal?with//?shutdownNow?race?while?clearing?interruptif?((runStateAtLeast(ctl.get(),?STOP)?||
?????????????????????(Thread.interrupted()?&&
??????????????????????runStateAtLeast(ctl.get(),?STOP)))?&&
????????????????????!wt.isInterrupted())
????????????????????wt.interrupt();try?{
????????????????????beforeExecute(wt,?task);
????????????????????Throwable?thrown?=?null;try?{
????????????????????????task.run();
????????????????????}?catch?(RuntimeException?x)?{
????????????????????????thrown?=?x;?throw?x;
????????????????????}?catch?(Error?x)?{
????????????????????????thrown?=?x;?throw?x;
????????????????????}?catch?(Throwable?x)?{
????????????????????????thrown?=?x;?throw?new?Error(x);
????????????????????}?finally?{
????????????????????????afterExecute(task,?thrown);
????????????????????}
????????????????}?finally?{
????????????????????task?=?null;
????????????????????w.completedTasks++;
????????????????????w.unlock();
????????????????}
????????????}
????????????completedAbruptly?=?false;
????????}?finally?{
????????????processWorkerExit(w,?completedAbruptly);
????????}
????}就是任務在并不只執行創建時指定的firstTask第一任務,還會從任務隊列的中自己主動取任務執行,而且是有或者無時間限定的阻塞等待,以保證線程的存活。默認的是不允許。2.4 CountDownLatch和CyclicBarrier區別
countDownLatch是一個計數器,線程完成一個記錄一個,計數器遞減,只能只用一次。
CyclicBarrier的計數器更像一個閥門,需要所有線程都到達,然后繼續執行,計數器遞增,提供Reset功能,可以多次使用。
3. 多線程間通信的幾種方式提及多線程又不得不提及多線程通信的機制。首先,要短信線程間通信的模型有兩種:共享內存和消息傳遞,以下方式都是基本這兩種模型來實現的。我們來基本一道面試常見的題目來分析:題目:有兩個線程A、B,A線程向一個集合里面依次添加元素"abc"字符串,一共添加十次,當添加到第五次的時候,希望B線程能夠收到A線程的通知,然后B線程執行相關的業務操作。3.1使用volatile關鍵字package?thread;/**?*?
?*?@author?hxz
?*?@description?多線程測試類
?*?@version?1.0
?*?@data?2020年2月15日?上午9:10:09
?*/public?class?MyThreadTest?{public?static?void?main(String[]?args)?throws?Exception?{
????????notifyThreadWithVolatile();
????}/**
?????*?定義一個測試
?????*/private?static?volatile?boolean?flag?=?false;/**
?????*?計算I++,當I==5時,通知線程B
?????*?@throws?Exception
?????*/private?static?void?notifyThreadWithVolatile()?throws?Exception?{
????????Thread?thc?=?new?Thread("線程A"){@Overridepublic?void?run()?{for?(int?i?=?0;?i?10;?i++)?{if?(i?==?5)?{
????????????????????????flag?=?true;try?{
????????????????????????????Thread.sleep(500L);
????????????????????????}?catch?(InterruptedException?e)?{//?TODO?Auto-generated?catch?block
????????????????????????????e.printStackTrace();
????????????????????????}break;
????????????????????}
????????????????????System.out.println(Thread.currentThread().getName()?+?"===="?+?i);
????????????????}
????????????}
????????};
????????Thread?thd?=?new?Thread("線程B")?{@Overridepublic?void?run()?{while?(true)?{//?防止偽喚醒?所以使用了whilewhile?(flag)?{
????????????????????????System.out.println(Thread.currentThread().getName()?+?"收到通知");
????????????????????????System.out.println("do?something");try?{
????????????????????????????Thread.sleep(500L);
????????????????????????}?catch?(InterruptedException?e)?{//?TODO?Auto-generated?catch?block
????????????????????????????e.printStackTrace();
????????????????????????}return?;
????????????????????}
????????????????}
????????????}
????????};
????????thd.start();
????????Thread.sleep(1000L);
????????thc.start();
????}
}個人認為這是基本上最好的通信方式,因為A發出通知B能夠立馬接受并Do Something。原文鏈接:https://blog.csdn.net/weixin_44104367/article/details/104481510【End】
《原力計劃【第二季】-?學習力挑戰》正式開始!
即日起至?3月21日,千萬流量支持原創作者,更有專屬【勛章】等你來挑戰
?用于小型圖形挖掘研究的瑞士軍刀:空手道俱樂部的圖表學習Python庫
?羅永浩欲直播帶貨,京東說可以幫忙聯系?MySQL數據庫無完整備份刪庫,除了跑路還能怎么辦?
?Libra新編程語言 :Move 的所有權模型靈感來源原來是它……
你點的每一個在看,我認真當成了喜歡總結
以上是生活随笔為你收集整理的找不到libmmd.dll无法继续执行代码_300 行代码带你秒懂 Java 多线程!| 原力计划...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java离职交接文档_财务人员工作交接你
- 下一篇: 如何升级浏览器_前谷歌员工爆料:谷歌工程