一篇文章帶你Java多線程入門
多線程的四種創(chuàng)建方式
1.繼承Thread類
/* * 多線程的創(chuàng)建,方式一:繼承Thread類 * 1.創(chuàng)建一個繼承于Thread類的子類 * 2,重寫Thread類的run() 將線程操作寫在run方法中 * 3.創(chuàng)建Thread類的子類的對象 * 4.通過對象調(diào)用start() */ //創(chuàng)建一個繼承于Thread類的子類 public class MThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { if(i%2==0) System.out.println(Thread.currentThread().getName()+":"+i); } } } public class Threadtest { public static void main(String[] args) { //3.創(chuàng)建Thread類的子類的對象 MThread t1=new MThread(); //4.通過此對象調(diào)用start() t1.start(); //start方法來實現(xiàn)啟動線程,并調(diào)用run方法,從而真正實現(xiàn)了多線程。同時run方法它只是一個普通的函數(shù)方法,不需要線程調(diào)用start方法也可以調(diào)用它. } }
2.實現(xiàn)Runnable接口
/* 1.創(chuàng)建一個實現(xiàn)了Runnable接口 2.實現(xiàn)類去實現(xiàn)Runable接口的類 3.創(chuàng)建實現(xiàn)類的對象 4.將此對象作為參數(shù)傳遞到Thread類的構造器中,創(chuàng)建Thread類的對象 5.通過Thread類的對象調(diào)用start() */ class window implements Runnable{ private int ticket =100; public void run() { while(true) { if(ticket >0) { System.out.println(Thread.currentThread().getName()+"賣票,票號為:"+ticket); ticket--; }else break; } } } public class RunnableTest { public static void main(String[] args) { window w=new window(); //三個線程用的同一個window,都執(zhí)行同一個window的run,因此在設置票的數(shù)量時不需要設置為static Thread t1=new Thread(w); Thread t2=new Thread(w); Thread t3=new Thread(w); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
3.實現(xiàn)Callable接口
/* * 創(chuàng)建線程方式三 實現(xiàn)Callable接口 * 實現(xiàn)Callable接口的方式強于實現(xiàn)Runnable接口的方式 *1.call()可以有返回值。 *2.call()可以拋出異常。 *3.callable支持泛型。 */ //1.創(chuàng)建一個實現(xiàn)Callable的實現(xiàn)類 class NumThread implements Callable{ //2.實現(xiàn)call方法,將此線程需要執(zhí)行的操作聲明在call()方法中 public Object call() throws Exception{ int sum=0; for(int i=0;i<=100;i++) { if(i%2==0) { System.out.println(i); sum+=i; } } return sum; } } public class CallableWay { public static void main(String[] args) { //3.創(chuàng)建Callable接口實現(xiàn)類的對象 NumThread numThread =new NumThread(); //4.將此Callable接口實現(xiàn)類的對象作為傳遞到FutureTask構造器中,創(chuàng)建FutureTask對象 FutureTask futureTask =new FutureTask(numThread); //5.將FutureTask的對象作為參數(shù)傳遞到Thread類的構造器中,創(chuàng)建Thread對象,并調(diào)用start()方法 new Thread(futureTask).start(); try { //6.獲取Callable中call()的返回值 //get()返回值即為FutureTask構造參數(shù)Callable實現(xiàn)類重寫的call()的返回值 Object sum = futureTask.get(); System.out.println("總和為:"+sum); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
4.使用線程池
/* * 創(chuàng)建線程的方式四:使用線程池 * 好處: * 1.提高響應速度(提高了創(chuàng)建新線程的時間) * 2.降低資源的消耗(重復利用線程池中線程,不需要每次都創(chuàng)建) * 3.便于線程管理 */ class NumberThread implements Runnable{ public void run() { for(int i=0;i<=100;i++) { if(i%2==0) { System.out.println(Thread.currentThread().getName()+":"+i); } } } } public class Thread4 { public static void main(String[] args) { //1.提供指定線程數(shù)量的線程池 ExecutorService service =Executors.newScheduledThreadPool(10); //2.執(zhí)行指定的線程的操作,需要提供實現(xiàn)Runnable接口或Callable接口實現(xiàn)類的對象 service.execute(new NumberThread()); //適用于Runnable //service.submit(); //適用于Callable //3.關閉線程池 service.shutdown(); } }
線程的優(yōu)先級
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5 —>默認優(yōu)先級
如何獲取和設置當前線程優(yōu)先級:
getpriority() //獲取線程的優(yōu)先級 setpriority(int p) //設置線程的優(yōu)先級
說明:高優(yōu)先級的線程要搶占低優(yōu)先級線程cpu的執(zhí)行權。但是只是從概率上來講,高優(yōu)先級的線程高概率的情況下被執(zhí)行,并不意味著只有高優(yōu)先級的線程執(zhí)行完后,低優(yōu)先級的線程才執(zhí)行。
測試Thread中常用的方法
1.star():啟動當前線程;調(diào)用當前線程的run()
2.run():通常需要重寫Thread類中的此方法,將創(chuàng)建的線程要執(zhí)行的操作聲明在此方法中
3.currentThread():靜態(tài)方法,返回執(zhí)行當前代碼的線程
4.getName():獲取當前線程的名字
5.setName():設置當前線程的名字
6.yield():釋放當前線程cpu的執(zhí)行權,各個線程重新“競爭”7.join():在線程a中調(diào)用線程b的join(),此時線程a就進入阻塞狀態(tài),直到線程b完全執(zhí)行完之后,線程a才結束阻塞狀態(tài)
8.stop():已過時。當執(zhí)行此方法時,強制結束當前線程
9.sleep(long millitime):讓當前線程“睡眠”指定millitime毫秒。在指定的時間內(nèi),當前線程是處于阻塞狀態(tài)
10.isAlive():判斷當前線程是否存活
線程的生命周期
多線程的同步控制
1.同步代碼塊
synchronized(同步監(jiān)視器){ // 需要被同步的代碼 }
下面展示實現(xiàn)Runnable接口的情況。
class TicketWindow implements Runnable { private int ticket = 100; private Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 賣出第 " + ticket + " 張票"); ticket--; } else { break; } } } } } public class ThreadSync { public static void main(String[] args) { TicketWindow ticketWindow = new TicketWindow(); Thread thread1 = new Thread(ticketWindow); Thread thread2 = new Thread(ticketWindow); Thread thread3 = new Thread(ticketWindow); thread1.setName("售票窗口1"); thread2.setName("售票窗口2"); thread3.setName("售票窗口3"); thread1.start(); thread2.start(); thread3.start(); } }
下面展示繼承Thread類的情況。
class TicketWindow1 extends Thread { private static int ticket = 100; private static Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 賣出第 " + ticket + " 張票"); ticket--; } else { break; } } } }}public class ThreadSync1 { public static void main(String[] args) { Thread thread1 = new TicketWindow1(); Thread thread2 = new TicketWindow1(); Thread thread3 = new TicketWindow1(); thread1.setName("售票窗口1"); thread2.setName("售票窗口2"); thread3.setName("售票窗口3"); thread1.start(); thread2.start(); thread3.start(); }}class TicketWindow1 extends Thread { private static int ticket = 100; private static Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 賣出第 " + ticket + " 張票"); ticket--; } else { break; } } } } } public class ThreadSync1 { public static void main(String[] args) { Thread thread1 = new TicketWindow1(); Thread thread2 = new TicketWindow1(); Thread thread3 = new TicketWindow1(); thread1.setName("售票窗口1"); thread2.setName("售票窗口2"); thread3.setName("售票窗口3"); thread1.start(); thread2.start(); thread3.start(); } }
對比二者不同二者不同在于synchronized(同步監(jiān)視器)中的同步監(jiān)視器,
實現(xiàn)Runnable接口的情況下,同步監(jiān)視器不需要用static,因為Runnable接口的實現(xiàn)類只被創(chuàng)建一次,三個線程的同步監(jiān)視器是同一個。而繼承Thread類的情況下,同步監(jiān)視器如不聲明為static則被聲明了三次,三個線程的同步監(jiān)視器不是同一。
2.同步方法
訪問修飾符 synchronized 返回值 方法名(參數(shù)列表) { // 同步代碼塊 }
下面展示實現(xiàn)Runnable接口的情況。
class TicketWindow implements Runnable { private int ticket = 100; @Override public void run() { while (ticket > 0) { sellTicket(); } } public synchronized void sellTicket() { if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 賣出第 " + ticket + " 張票"); ticket--; } } }
下面展示繼承Thread類的情況。
class TicketWindow1 extends Thread { private static int ticket = 100; @Override public void run() { while (ticket > 0) { sellTicket(); } } /** * 通過繼承Thread類實現(xiàn)的多線程,同步方法必須為靜態(tài)方法,因為非靜態(tài)的同步方法,同步監(jiān)視器為this, * Thread thread1 = new TicketWindow1(); * Thread thread2 = new TicketWindow1(); * Thread thread3 = new TicketWindow1(); * 而上述的this不唯一,因此無法實現(xiàn)對共享資源的互斥訪問。 */ public static synchronized void sellTicket() { if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 賣出第 " + ticket + " 張票"); ticket--; } } }
3.同步鎖
// 1 獲得一個鎖 Lock lock = new ReentrantLock(); // 2 加鎖 lock.lock(); // 同步代碼塊 // 3 解鎖 lock.unlock();
下面展示代碼實現(xiàn)。
class TicketWindowLock implements Runnable { private int ticket = 100; // 1 獲得一個鎖 private Lock lock = new ReentrantLock(); /** * private ReentrantLock lock = new ReentrantLock(true); * 帶參數(shù)的構造方法:ReentrantLock(boolean fair); ==> 公平鎖 * 所謂公平鎖:假設現(xiàn)在三個賣票線程按 1、2、3 順序先后到達并爭取鎖,但是窗口1獲得了鎖并賣票, * 窗口2、3等待,等窗口1釋放鎖后,窗口2、3再按順序獲得鎖并賣票,保證按照先到先得的順序獲得鎖, * 以此保證公平性。 * * 若是使用無參構造方法獲得鎖,則不保證公平性。同樣的,三個賣票線程按 1、2、3 順序先后到達并 * 爭取鎖,但是窗口1獲得了鎖并賣票,窗口2、3等待,窗口1釋放鎖后,有可能再次獲得鎖并賣票,窗 * 口2、3仍然等待,不保證公平性。 */ @Override public void run() { while (true) { try { // 2 上鎖 lock.lock(); if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 賣出第 " + ticket + " 張票"); ticket--; } else { break; } } catch (Exception e) { e.printStackTrace(); } finally { // 3 解鎖。使用 try catch finally,將解鎖操作放在finally語句塊中,保證鎖一定會被釋放 lock.unlock(); } } } }
synchronized與lock鎖有何異同
- 相同:二者都是用來解決線程安全問題
- 不同:synchronized機制在執(zhí)行完相應的同步代碼以后,自動的釋放同步監(jiān)視器,lock需要手動的打開釋放。
線程通信
wait/notify模式
涉及到的三個方法
1.wait()
:一旦執(zhí)行此方法,當前線程就會進入阻塞狀態(tài),并且釋放同步監(jiān)視器。
2.notify()
:一旦執(zhí)行此方法,就會喚醒被wait的一個線程,如果有多個線程被wait,就優(yōu)先喚醒高優(yōu)先級的線程
3.notifyAll()
:一旦執(zhí)行此方法,所有的線程都會被喚醒
說明上述三個方法都必須使用在同步代碼塊或同步方法中的同步監(jiān)視器中,否則會出現(xiàn)異常
上述三個方法都是定義在Object類中的。
sleep和wait的異同
相同點:一旦執(zhí)行方法,都可以使得當前的線程進入阻塞狀態(tài)。
不同點:
1)倆個方法聲明的位置不同:Thread類中聲明sleep(),Object類中聲明wait()
2)調(diào)用的要求不同:sleep可以在任何場景下調(diào)用,而wait只能在同步代碼塊或者同步方法的同步監(jiān)視器中
3)關于是否釋放同步監(jiān)視器:如果倆個方法都使用在同步代碼塊或同步方法中,sleep()不釋放,而wait()釋放。
總結
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內(nèi)容!
相關文章
SpringMVC五種類型參數(shù)傳遞及json傳遞參數(shù)
本文主要介紹了SpringMVC五種類型參數(shù)傳遞及json傳遞參數(shù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07詳解基于spring多數(shù)據(jù)源動態(tài)調(diào)用及其事務處理
本篇文章主要介紹了基于spring多數(shù)據(jù)源動態(tài)調(diào)用及其事務處理 ,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06SpringBoot中使用spring-retry 解決失敗重試調(diào)用
本文主要介紹了SpringBoot中使用spring-retry 解決失敗重試調(diào)用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-07-07JVM默認時區(qū)為:Asia/Shanghai與java程序中GMT+08不一致異常
這篇文章主要介紹了JVM默認時區(qū)為:Asia/Shanghai與java程序中GMT+08不一致異常問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10