一篇文章帶你Java多線程入門
多線程的四種創(chuàng)建方式
1.繼承Thread類
/*
* 多線程的創(chuàng)建,方式一:繼承Thread類
* 1.創(chuàng)建一個(gè)繼承于Thread類的子類
* 2,重寫Thread類的run() 將線程操作寫在run方法中
* 3.創(chuàng)建Thread類的子類的對(duì)象
* 4.通過對(duì)象調(diào)用start()
*/
//創(chuàng)建一個(gè)繼承于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類的子類的對(duì)象
MThread t1=new MThread();
//4.通過此對(duì)象調(diào)用start()
t1.start(); //start方法來實(shí)現(xiàn)啟動(dòng)線程,并調(diào)用run方法,從而真正實(shí)現(xiàn)了多線程。同時(shí)run方法它只是一個(gè)普通的函數(shù)方法,不需要線程調(diào)用start方法也可以調(diào)用它.
}
}
2.實(shí)現(xiàn)Runnable接口
/*
1.創(chuàng)建一個(gè)實(shí)現(xiàn)了Runnable接口
2.實(shí)現(xiàn)類去實(shí)現(xiàn)Runable接口的類
3.創(chuàng)建實(shí)現(xiàn)類的對(duì)象
4.將此對(duì)象作為參數(shù)傳遞到Thread類的構(gòu)造器中,創(chuàng)建Thread類的對(duì)象
5.通過Thread類的對(duì)象調(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()+"賣票,票號(hào)為:"+ticket);
ticket--;
}else
break;
}
}
}
public class RunnableTest {
public static void main(String[] args) {
window w=new window();
//三個(gè)線程用的同一個(gè)window,都執(zhí)行同一個(gè)window的run,因此在設(shè)置票的數(shù)量時(shí)不需要設(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.實(shí)現(xiàn)Callable接口
/*
* 創(chuàng)建線程方式三 實(shí)現(xiàn)Callable接口
* 實(shí)現(xiàn)Callable接口的方式強(qiáng)于實(shí)現(xiàn)Runnable接口的方式
*1.call()可以有返回值。
*2.call()可以拋出異常。
*3.callable支持泛型。
*/
//1.創(chuàng)建一個(gè)實(shí)現(xiàn)Callable的實(shí)現(xiàn)類
class NumThread implements Callable{
//2.實(shí)現(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接口實(shí)現(xiàn)類的對(duì)象
NumThread numThread =new NumThread();
//4.將此Callable接口實(shí)現(xiàn)類的對(duì)象作為傳遞到FutureTask構(gòu)造器中,創(chuàng)建FutureTask對(duì)象
FutureTask futureTask =new FutureTask(numThread);
//5.將FutureTask的對(duì)象作為參數(shù)傳遞到Thread類的構(gòu)造器中,創(chuàng)建Thread對(duì)象,并調(diào)用start()方法
new Thread(futureTask).start();
try {
//6.獲取Callable中call()的返回值
//get()返回值即為FutureTask構(gòu)造參數(shù)Callable實(shí)現(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.提高響應(yīng)速度(提高了創(chuàng)建新線程的時(shí)間)
* 2.降低資源的消耗(重復(fù)利用線程池中線程,不需要每次都創(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í)行指定的線程的操作,需要提供實(shí)現(xiàn)Runnable接口或Callable接口實(shí)現(xiàn)類的對(duì)象
service.execute(new NumberThread()); //適用于Runnable
//service.submit(); //適用于Callable
//3.關(guān)閉線程池
service.shutdown();
}
}
線程的優(yōu)先級(jí)
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5 —>默認(rèn)優(yōu)先級(jí)
如何獲取和設(shè)置當(dāng)前線程優(yōu)先級(jí):
getpriority() //獲取線程的優(yōu)先級(jí) setpriority(int p) //設(shè)置線程的優(yōu)先級(jí)
說明:高優(yōu)先級(jí)的線程要搶占低優(yōu)先級(jí)線程cpu的執(zhí)行權(quán)。但是只是從概率上來講,高優(yōu)先級(jí)的線程高概率的情況下被執(zhí)行,并不意味著只有高優(yōu)先級(jí)的線程執(zhí)行完后,低優(yōu)先級(jí)的線程才執(zhí)行。
測(cè)試Thread中常用的方法
1.star():?jiǎn)?dòng)當(dāng)前線程;調(diào)用當(dāng)前線程的run()
2.run():通常需要重寫Thread類中的此方法,將創(chuàng)建的線程要執(zhí)行的操作聲明在此方法中
3.currentThread():靜態(tài)方法,返回執(zhí)行當(dāng)前代碼的線程
4.getName():獲取當(dāng)前線程的名字
5.setName():設(shè)置當(dāng)前線程的名字
6.yield():釋放當(dāng)前線程cpu的執(zhí)行權(quán),各個(gè)線程重新“競(jìng)爭(zhēng)”7.join():在線程a中調(diào)用線程b的join(),此時(shí)線程a就進(jìn)入阻塞狀態(tài),直到線程b完全執(zhí)行完之后,線程a才結(jié)束阻塞狀態(tài)
8.stop():已過時(shí)。當(dāng)執(zhí)行此方法時(shí),強(qiáng)制結(jié)束當(dāng)前線程
9.sleep(long millitime):讓當(dāng)前線程“睡眠”指定millitime毫秒。在指定的時(shí)間內(nèi),當(dāng)前線程是處于阻塞狀態(tài)
10.isAlive():判斷當(dāng)前線程是否存活
線程的生命周期

多線程的同步控制
1.同步代碼塊
synchronized(同步監(jiān)視器){
// 需要被同步的代碼
}
下面展示實(shí)現(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();
}
}
對(duì)比二者不同二者不同在于synchronized(同步監(jiān)視器)中的同步監(jiān)視器,
實(shí)現(xiàn)Runnable接口的情況下,同步監(jiān)視器不需要用static,因?yàn)镽unnable接口的實(shí)現(xiàn)類只被創(chuàng)建一次,三個(gè)線程的同步監(jiān)視器是同一個(gè)。而繼承Thread類的情況下,同步監(jiān)視器如不聲明為static則被聲明了三次,三個(gè)線程的同步監(jiān)視器不是同一。
2.同步方法
訪問修飾符 synchronized 返回值 方法名(參數(shù)列表) {
// 同步代碼塊
}
下面展示實(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類實(shí)現(xiàn)的多線程,同步方法必須為靜態(tài)方法,因?yàn)榉庆o態(tài)的同步方法,同步監(jiān)視器為this,
* Thread thread1 = new TicketWindow1();
* Thread thread2 = new TicketWindow1();
* Thread thread3 = new TicketWindow1();
* 而上述的this不唯一,因此無法實(shí)現(xiàn)對(duì)共享資源的互斥訪問。
*/
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 獲得一個(gè)鎖 Lock lock = new ReentrantLock(); // 2 加鎖 lock.lock(); // 同步代碼塊 // 3 解鎖 lock.unlock();
下面展示代碼實(shí)現(xiàn)。
class TicketWindowLock implements Runnable {
private int ticket = 100;
// 1 獲得一個(gè)鎖
private Lock lock = new ReentrantLock();
/**
* private ReentrantLock lock = new ReentrantLock(true);
* 帶參數(shù)的構(gòu)造方法:ReentrantLock(boolean fair); ==> 公平鎖
* 所謂公平鎖:假設(shè)現(xiàn)在三個(gè)賣票線程按 1、2、3 順序先后到達(dá)并爭(zhēng)取鎖,但是窗口1獲得了鎖并賣票,
* 窗口2、3等待,等窗口1釋放鎖后,窗口2、3再按順序獲得鎖并賣票,保證按照先到先得的順序獲得鎖,
* 以此保證公平性。
*
* 若是使用無參構(gòu)造方法獲得鎖,則不保證公平性。同樣的,三個(gè)賣票線程按 1、2、3 順序先后到達(dá)并
* 爭(zhēng)取鎖,但是窗口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語句塊中,保證鎖一定會(huì)被釋放
lock.unlock();
}
}
}
}
synchronized與lock鎖有何異同
- 相同:二者都是用來解決線程安全問題
- 不同:synchronized機(jī)制在執(zhí)行完相應(yīng)的同步代碼以后,自動(dòng)的釋放同步監(jiān)視器,lock需要手動(dòng)的打開釋放。
線程通信
wait/notify模式
涉及到的三個(gè)方法
1.wait():一旦執(zhí)行此方法,當(dāng)前線程就會(huì)進(jìn)入阻塞狀態(tài),并且釋放同步監(jiān)視器。
2.notify():一旦執(zhí)行此方法,就會(huì)喚醒被wait的一個(gè)線程,如果有多個(gè)線程被wait,就優(yōu)先喚醒高優(yōu)先級(jí)的線程
3.notifyAll():一旦執(zhí)行此方法,所有的線程都會(huì)被喚醒
說明上述三個(gè)方法都必須使用在同步代碼塊或同步方法中的同步監(jiān)視器中,否則會(huì)出現(xiàn)異常
上述三個(gè)方法都是定義在Object類中的。
sleep和wait的異同
相同點(diǎn):一旦執(zhí)行方法,都可以使得當(dāng)前的線程進(jìn)入阻塞狀態(tài)。
不同點(diǎn):
1)倆個(gè)方法聲明的位置不同:Thread類中聲明sleep(),Object類中聲明wait()
2)調(diào)用的要求不同:sleep可以在任何場(chǎng)景下調(diào)用,而wait只能在同步代碼塊或者同步方法的同步監(jiān)視器中
3)關(guān)于是否釋放同步監(jiān)視器:如果倆個(gè)方法都使用在同步代碼塊或同步方法中,sleep()不釋放,而wait()釋放。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
java實(shí)現(xiàn)上傳文件類型檢測(cè)過程解析
這篇文章主要介紹了java實(shí)現(xiàn)上傳文件類型檢測(cè)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
超詳細(xì)講解Java秒殺項(xiàng)目用戶驗(yàn)證模塊的實(shí)現(xiàn)
這是一個(gè)主要使用java開發(fā)的秒殺系統(tǒng),項(xiàng)目比較大,所以本篇只實(shí)現(xiàn)了用戶驗(yàn)證模塊,代碼非常詳盡,感興趣的朋友快來看看2022-03-03
詳解多云架構(gòu)下的JAVA微服務(wù)技術(shù)解析
本文介紹了基于開源自建和適配云廠商開發(fā)框架兩種構(gòu)建多云架構(gòu)的思路,以及這些思路的優(yōu)缺點(diǎn)2021-05-05
java實(shí)現(xiàn)簡(jiǎn)單石頭剪刀布小游戲
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單石頭剪刀布小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
SpringMVC五種類型參數(shù)傳遞及json傳遞參數(shù)
本文主要介紹了SpringMVC五種類型參數(shù)傳遞及json傳遞參數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
詳解基于spring多數(shù)據(jù)源動(dòng)態(tài)調(diào)用及其事務(wù)處理
本篇文章主要介紹了基于spring多數(shù)據(jù)源動(dòng)態(tài)調(diào)用及其事務(wù)處理 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
Spring?WebClient實(shí)戰(zhàn)示例
本文主要介紹了Spring?WebClient實(shí)戰(zhàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
SpringBoot中使用spring-retry 解決失敗重試調(diào)用
本文主要介紹了SpringBoot中使用spring-retry 解決失敗重試調(diào)用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
JVM默認(rèn)時(shí)區(qū)為:Asia/Shanghai與java程序中GMT+08不一致異常
這篇文章主要介紹了JVM默認(rèn)時(shí)區(qū)為:Asia/Shanghai與java程序中GMT+08不一致異常問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10

