【Java基础】一篇文章读懂多线程
1.多線程
1.1并發(fā)和并行
- 并發(fā):兩個或多個事件在同一時間段發(fā)生
- 并行:兩個或多個事件在同一時刻發(fā)生
1.2線程和進(jìn)程
- 進(jìn)程是程序的一次執(zhí)行過程,進(jìn)程是系統(tǒng)運行應(yīng)用程序的基本單位,一個應(yīng)用程序可以同時運行多個進(jìn)程
- 線程是進(jìn)程中的一個執(zhí)行單元,一個進(jìn)程至少由一個線程,負(fù)責(zé)程序的執(zhí)行
1.3線程調(diào)度的兩種方式
- 分時調(diào)度:所有線程輪流使用CPU,平均分配每個線程使用CPU的時間
- 搶占式調(diào)度:Java讓優(yōu)先級高的線程執(zhí)行,優(yōu)先級相同則隨機選擇一個線程執(zhí)行
1.4創(chuàng)建多線程的兩種方式
-
多個線程在不同的棧空間里執(zhí)行,互不影響
-
第一種實現(xiàn)方式:創(chuàng)建Thread的子類,并且重寫run方法,一般不用
- 第二種實現(xiàn)方式:實現(xiàn)Runnable接口,重寫run方法,使用Thread(Runnable target)分配Thread對象
- 實現(xiàn)Runnable接口創(chuàng)建多線程的好處
- 避免單繼承的局限性
- 將設(shè)置線程任務(wù)和開啟線程兩個過程解耦
1.5獲取線程名稱的兩種方式
- String getName() 返回線程名稱
- static Thread currentThread() 返回當(dāng)前正在執(zhí)行線程的引用
2.線程安全
2.1線程同步
-
當(dāng)使用多線程訪問同一資源時,且多個線程對資源有寫操作,容易出現(xiàn)線程安全問題,Java中使用同步機制解決
public class Main {public static void main(String[] args) {RunnableImpl runnable = new RunnableImpl();//三個線程共享一個實現(xiàn)類對象,保證共享資源為100張票new Thread(runnable).start();new Thread(runnable).start();new Thread(runnable).start();//出現(xiàn)重復(fù)的票和不存在的票} }class RunnableImpl implements Runnable {//定義一個多線程共享的資源private int ticket = 100;//設(shè)置線程任務(wù)買票@Overridepublic void run() {//讓買票操作重復(fù)執(zhí)行while (true) {//先判斷票是否存在if (ticket > 0) {//提高安全問題出現(xiàn)的概率try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//執(zhí)行賣票操作System.out.println(Thread.currentThread().getName() + "-->" + ticket);ticket--;}}} } -
同步代碼塊解決線程安全問題:把可能出現(xiàn)安全問題的代碼塊用synchronized修飾
public class Main {public static void main(String[] args) {RunnableImpl runnable = new RunnableImpl();//三個線程共享一個實現(xiàn)類對象,保證共享資源為100張票new Thread(runnable).start();new Thread(runnable).start();new Thread(runnable).start();//出現(xiàn)重復(fù)的票和不存在的票} }class RunnableImpl implements Runnable {//定義一個多線程共享的資源private int ticket = 100;//創(chuàng)建一個鎖對象Object object = new Object();//設(shè)置線程任務(wù)買票@Overridepublic void run() {//讓買票操作重復(fù)執(zhí)行while (true) {synchronized (object) {//先判斷票是否存在if (ticket > 0) {//提高安全問題出現(xiàn)的概率try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//執(zhí)行賣票操作System.out.println(Thread.currentThread().getName() + "-->" + ticket);ticket--;}}}} } -
同步方法解決線程安全問題:把可能出現(xiàn)安全問題的代碼塊形成一個方法并用synchronized修飾該方法
public class Main {public static void main(String[] args) {RunnableImpl runnable = new RunnableImpl();//三個線程共享一個實現(xiàn)類對象,保證共享資源為100張票new Thread(runnable).start();new Thread(runnable).start();new Thread(runnable).start();//出現(xiàn)重復(fù)的票和不存在的票} }class RunnableImpl implements Runnable {//定義一個多線程共享的資源private int ticket = 100;//創(chuàng)建一個同步方法public synchronized void sellTicket() {//先判斷票是否存在if (ticket > 0) {//提高安全問題出現(xiàn)的概率try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//執(zhí)行賣票操作System.out.println(Thread.currentThread().getName() + "-->" + ticket);ticket--;}}//設(shè)置線程任務(wù)買票@Overridepublic void run() {//讓買票操作重復(fù)執(zhí)行while (true) {sellTicket();}} } -
鎖機制解決線程安全問題:Lock接口實現(xiàn)
public class Main {public static void main(String[] args) {RunnableImpl runnable = new RunnableImpl();//三個線程共享一個實現(xiàn)類對象,保證共享資源為100張票new Thread(runnable).start();new Thread(runnable).start();new Thread(runnable).start();//出現(xiàn)重復(fù)的票和不存在的票} }class RunnableImpl implements Runnable {//定義一個多線程共享的資源private int ticket = 100;//創(chuàng)建一個鎖對象Lock lock = new ReentrantLock();//設(shè)置線程任務(wù)買票@Overridepublic void run() {//讓買票操作重復(fù)執(zhí)行while (true) {lock.lock();//加鎖//先判斷票是否存在if (ticket > 0) {//提高安全問題出現(xiàn)的概率try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//執(zhí)行賣票操作System.out.println(Thread.currentThread().getName() + "-->" + ticket);ticket--;}lock.unlock();//解鎖}} }
3.線程狀態(tài)
3.1等待喚醒案例:線程之間的通信
-
創(chuàng)建一個消費者線程,調(diào)用wait方法之后,放棄CPU進(jìn)入WAITING狀態(tài)(無限等待)
-
創(chuàng)建一個生產(chǎn)者線程,調(diào)用Notify方法喚醒消費者進(jìn)程
/*** @ClassName WaitAndNotify* @Description TODO* @Author hulin* @Date 2020/1/23 21:11* @Version 1.0.0*/ /*進(jìn)入到TimeWaiting(計時等待)有兩種方式1.使用sleep(long m)方法,在毫秒值結(jié)束之后,線程睡醒進(jìn)入到Runnable/Blocked狀態(tài)2.使用wait(long m)方法,wait方法如果在毫秒值結(jié)束之后,還沒有被notify喚醒,就會自動醒來,線程睡醒進(jìn)入到Runnable/Blocked狀態(tài)喚醒的方法:void notify() 喚醒在此對象監(jiān)視器上等待的單個線程。void notifyAll() 喚醒在此對象監(jiān)視器上等待的所有線程。*/ public class WaitAndNotify {public static void main(String[] args) {//創(chuàng)建鎖對象,保證唯一Object obj = new Object();// 創(chuàng)建一個顧客線程(消費者)new Thread(){@Overridepublic void run() {//一直等著買包子while(true){//保證等待和喚醒的線程只能有一個執(zhí)行,需要使用同步技術(shù)synchronized (obj){System.out.println("顧客1告知老板要的包子的種類和數(shù)量");//調(diào)用wait方法,放棄cpu的執(zhí)行,進(jìn)入到WAITING狀態(tài)(無限等待)try {obj.wait();} catch (InterruptedException e) {e.printStackTrace();}//喚醒之后執(zhí)行的代碼System.out.println("包子已經(jīng)做好了,顧客1開吃!");System.out.println("---------------------------------------");}}}}.start();// 創(chuàng)建一個顧客線程(消費者)new Thread(){@Overridepublic void run() {//一直等著買包子while(true){//保證等待和喚醒的線程只能有一個執(zhí)行,需要使用同步技術(shù)synchronized (obj){System.out.println("顧客2告知老板要的包子的種類和數(shù)量");//調(diào)用wait方法,放棄cpu的執(zhí)行,進(jìn)入到WAITING狀態(tài)(無限等待)try {obj.wait();} catch (InterruptedException e) {e.printStackTrace();}//喚醒之后執(zhí)行的代碼System.out.println("包子已經(jīng)做好了,顧客2開吃!");System.out.println("---------------------------------------");}}}}.start();//創(chuàng)建一個老板線程(生產(chǎn)者)new Thread(){@Overridepublic void run() {//一直做包子while (true){//花了5秒做包子try {Thread.sleep(5000);//花5秒鐘做包子} catch (InterruptedException e) {e.printStackTrace();}//保證等待和喚醒的線程只能有一個執(zhí)行,需要使用同步技術(shù)synchronized (obj){System.out.println("老板5秒鐘之后做好包子,告知顧客,可以吃包子了");//做好包子之后,調(diào)用notify方法,喚醒顧客吃包子//obj.notify();//如果有多個等待線程,隨機喚醒一個obj.notifyAll();//喚醒所有等待的線程}}}}.start();} }
3.2等待喚醒機制
- 多個線程在處理同一個資源且任務(wù)不同時,需要線程通信來幫助解決,需要線程通信來幫助解決線程之間對同一個資源的使用和操作。通過等待喚醒機制使各個線程有效的利用資源。
- wait方法和notify方法都屬于Object類的方法,必須在同步代碼塊或者同步函數(shù)中使用,必須由同一個鎖對象調(diào)用
4.線程池
4.1概念
- 容納多個線程的容器(ArrayList,LinkedList,HashSet),無需反復(fù)創(chuàng)建線程
- 好處:降低資源消耗,提高相應(yīng)速度,提高線程的可管理性
4.2線程池的實現(xiàn)
使用線程池的工廠類Executors的靜態(tài)方法生產(chǎn)一個指定數(shù)量的線程池
創(chuàng)建一個類實現(xiàn)Runnable接口,重寫run方法,設(shè)置線程任務(wù)
調(diào)用ExecutorService的方法submit傳遞線程任務(wù)(實現(xiàn)類),開啟線程執(zhí)行run方法
使用ExecutorService的方法shutdown銷毀線程池
總結(jié)
以上是生活随笔為你收集整理的【Java基础】一篇文章读懂多线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Anaconda安装和使用指南教程:环境
- 下一篇: java美元兑换,(Java实现) 美元