Java 多線程之兩步掌握
導(dǎo)論:初識(shí)多線程
首先,我們來討論討論什么叫做多線程。舉個(gè)簡單的例子,比如說造房子這個(gè)任務(wù)。如果只有一個(gè)人的話,他既要搬磚還得拎砂漿、攪拌水泥之類的(其他工種這里就不一一闡述了),哪怕這個(gè)工人技術(shù)再熟練,精力再旺盛,他同時(shí)也只能干一個(gè)工種。那么問題來了,該如何提升效率呢?很簡單,我們可以請(qǐng)多個(gè)工人同時(shí)來干活,可以同時(shí)干多種也可以干同種活兒,這樣效率就高得多。盡管他們各自可干著不同的活兒,但本質(zhì)都是為了造房子這個(gè)任務(wù),這就叫做多進(jìn)程,即將一個(gè)大任務(wù)拆分成不同的小任務(wù),分配不同的人來執(zhí)行,當(dāng)包工頭也就是處理器下達(dá)命令時(shí),他們按照指令來工作。
每個(gè)工種的話,比如攪拌水泥的大工優(yōu)惠叫來幾個(gè)小工,都是來干攪拌水泥這個(gè)活兒,所以這里叫做多線程。
那么,怎么區(qū)分多進(jìn)程和多線程呢?這里簡單概括:
進(jìn)程是系統(tǒng)分配資源的最小單位,線程是系統(tǒng)調(diào)度的最小單位。一個(gè)進(jìn)程內(nèi)的線程之間是可以共享資源的。 每個(gè)進(jìn)程至少有一個(gè)線程存在,即主線程。
一:動(dòng)手來創(chuàng)建多線程
1.1 創(chuàng)建一個(gè)主線程
請(qǐng)看代碼:
public class ThreadDemo1 {
static class MyThread extends Thread {
@Override
public void run() {
System.out.println("hello world, 我是一個(gè)線程");
while (true) {
}
}
}
public static void main(String[] args) {
// 創(chuàng)建線程需要使用 Thread 類, 來創(chuàng)建一個(gè) Thread 的實(shí)例.
// 另一方面還需要給這個(gè)線程指定, 要執(zhí)行哪些指令/代碼.
// 指定指令的方式有很多種方式, 此處先用一種簡單的, 直接繼承 Thread 類,
// 重寫 Thread 類中的 run 方法.
// [注意!] 當(dāng) Thread 對(duì)象被創(chuàng)建出來的時(shí)候, 內(nèi)核中并沒有隨之產(chǎn)生一個(gè)線程(PCB).
Thread t = new MyThread();
// 執(zhí)行這個(gè) start 方法, 才是真的創(chuàng)建出了一個(gè)線程.
// 此時(shí)內(nèi)核中才隨之出現(xiàn)了一個(gè) PCB, 這個(gè) PCB 就會(huì)對(duì)應(yīng)讓 CPU 來執(zhí)行該線程的代碼. (上面的 run 方法中的邏輯)
t.start();
while (true) {
// 這里啥都不干
}
}
}
接下里,我們可以通過jdk里面的一個(gè)jconsole來查看,我的文件路徑是C:\Program Files\Java\jdk1.8.0_192\bin,大家可以對(duì)照自己安裝的jdk文件位置來尋找。運(yùn)行程序,打開jconsole可以看下

這里這個(gè)主線程就是我們創(chuàng)建的線程,線程創(chuàng)建成功。
1.2 多線程搶占式執(zhí)行
創(chuàng)建兩個(gè)線程,輸出線程運(yùn)行前后時(shí)間,多次運(yùn)行發(fā)現(xiàn)運(yùn)行時(shí)間不一,這里就體現(xiàn)了現(xiàn)成的搶占式執(zhí)行方法,看代碼:
public class ThreadDemo2 {
private static long count = 100_0000_0000L;
public static void main(String[] args) {
// serial();
concurrency();
}
private static void serial() {
long beg = System.currentTimeMillis();
int a = 0;
for (long i = 0; i < count; i++) {
a++;
}
int b = 0;
for (long i = 0; i < count; i++) {
b++;
}
long end = System.currentTimeMillis();
System.out.println("time: " + (end - beg) + " ms");
}
private static void concurrency() {
long beg = System.currentTimeMillis();
Thread t1 = new Thread() {
@Override
public void run() {
int a = 0;
for (long i = 0; i < count; i++) {
a++;
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
int b = 0;
for (long i = 0; i < count; i++) {
b++;
}
}
};
t1.start();
t2.start();
try {
// 線程等待. 讓主線程等待 t1 和 t2 執(zhí)行結(jié)束, 然后再繼續(xù)往下執(zhí)行.
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// t1 t2 和 main 線程之間都是并發(fā)執(zhí)行的.
// 調(diào)用了 t1.start 和 t2.start 之后, 兩個(gè)新線程正在緊鑼密鼓的進(jìn)行計(jì)算過程中,
// 此時(shí)主線程仍然會(huì)繼續(xù)執(zhí)行, 下面的 end 就隨之被計(jì)算了.
// 正確的做法應(yīng)該是要保證 t1 和 t2 都計(jì)算完畢, 再來計(jì)算這個(gè) end 的時(shí)間戳.
long end = System.currentTimeMillis();
System.out.println("time: " + (end - beg) + " ms");
}
}
多次運(yùn)行,會(huì)有以下結(jié)果:


可以發(fā)現(xiàn)線程是搶占式執(zhí)行。
我們用join關(guān)鍵字,可以規(guī)定線程運(yùn)行先后順序,比如這里規(guī)定t1運(yùn)行完后t2再運(yùn)行,代碼如下:
public class ThreadDemo2 {
private static long count = 100_0000_0000L;
public static void main(String[] args) {
serial();
//concurrency();
}
private static void serial() {
long beg = System.currentTimeMillis();
int a = 0;
for (long i = 0; i < count; i++) {
a++;
}
int b = 0;
for (long i = 0; i < count; i++) {
b++;
}
long end = System.currentTimeMillis();
System.out.println("time: " + (end - beg) + " ms");
}
private static void concurrency() {
long beg = System.currentTimeMillis();
Thread t1 = new Thread() {
@Override
public void run() {
int a = 0;
for (long i = 0; i < count; i++) {
a++;
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
int b = 0;
for (long i = 0; i < count; i++) {
b++;
}
}
};
t1.start();
t2.start();
try {
// 線程等待. 讓主線程等待 t1 和 t2 執(zhí)行結(jié)束, 然后再繼續(xù)往下執(zhí)行.
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// t1 t2 和 main 線程之間都是并發(fā)執(zhí)行的.
// 調(diào)用了 t1.start 和 t2.start 之后, 兩個(gè)新線程正在緊鑼密鼓的進(jìn)行計(jì)算過程中,
// 此時(shí)主線程仍然會(huì)繼續(xù)執(zhí)行, 下面的 end 就隨之被計(jì)算了.
// 正確的做法應(yīng)該是要保證 t1 和 t2 都計(jì)算完畢, 再來計(jì)算這個(gè) end 的時(shí)間戳.
long end = System.currentTimeMillis();
System.out.println("time: " + (end - beg) + " ms");
}
}
多次運(yùn)行,結(jié)果如下:


這里發(fā)現(xiàn),由于規(guī)定了線程運(yùn)行先后時(shí)間,導(dǎo)致運(yùn)行時(shí)間大大增長,由此體現(xiàn)了線程并發(fā)運(yùn)行的優(yōu)勢(shì)所在。
這里說明一點(diǎn):由于線程是搶占式執(zhí)行,所以每次結(jié)果都是不一定的,但誤差會(huì)在一定范圍內(nèi)。
二:創(chuàng)建線程的幾個(gè)常用方法
2.2 繼承 Thread 類
可以通過繼承 Thread 來創(chuàng)建一個(gè)線程類,該方法的好處是 this 代表的就是當(dāng)前線程,不需要通過 Thread.currentThread() 來獲取當(dāng)前線程的引用。
class MyThread extends Thread {
@Override
public void run ()
{ System . out . println ( " 這里是線程運(yùn)行的代碼 " );
}
}
MyThread t = new MyThread ();
t . start (); // 線程開始運(yùn)行
2.2 實(shí)現(xiàn) Runnable 接口
通過實(shí)現(xiàn) Runnable 接口,并且調(diào)用 Thread 的構(gòu)造方法時(shí)將 Runnable 對(duì)象作為 target 參數(shù)傳入來創(chuàng)建線程對(duì)象。 該方法的好處是可以規(guī)避類的單繼承的限制;但需要通過 Thread.currentThread() 來獲取當(dāng)前線程的引用。
class MyRunnable implements Runnable {
@Override
public void run () {
System . out . println ( Thread . currentThread (). getName () + " 這里是線程運(yùn)行的代碼 " );
}
}
Thread t = new Thread(new MyRunnable());
t.start(); // 線程開始運(yùn)行
2.3 匿名類創(chuàng)建
// 使用匿名類創(chuàng)建 Thread 子類對(duì)象
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println(" 使用匿名類創(chuàng)建 Thread 子類對(duì)象 ");
}
};
// 使用匿名類創(chuàng)建 Runnable 子類對(duì)象
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(" 使用匿名類創(chuàng)建 Runnable 子類對(duì)象 ");
}
});
// 使用 lambda 表達(dá)式創(chuàng)建 Runnable 子類對(duì)象
Thread t3 = new Thread(() -> System.out.println(" 使用匿名類創(chuàng)建 Thread 子類對(duì)象 "));
Thread t4 = new Thread(() -> {
System.out.println(" 使用匿名類創(chuàng)建 Thread 子類對(duì)象 ");
});
三:Thread的幾個(gè)常見屬性

代碼如下:
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + ": 我還活著");
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": 我即將死去");
});
thread.start();
System.out.println(Thread.currentThread().getName()
+ ": ID: " + thread.getId());
System.out.println(Thread.currentThread().getName()
+ ": 名稱: " + thread.getName());
System.out.println(Thread.currentThread().getName()
+ ": 狀態(tài): " + thread.getState());
System.out.println(Thread.currentThread().getName()
+ ": 優(yōu)先級(jí): " + thread.getPriority());
System.out.println(Thread.currentThread().getName()
+ ": 后臺(tái)線程: " + thread.isDaemon());
System.out.println(Thread.currentThread().getName()
+ ": 活著: " + thread.isAlive());
System.out.println(Thread.currentThread().getName()
+ ": 被中斷: " + thread.isInterrupted());
while (thread.isAlive()) {}
System.out.println(Thread.currentThread().getName()
+ ": 狀態(tài): " + thread.getState());
}
}
運(yùn)行結(jié)果:

由此可見各個(gè)關(guān)鍵字的含義,今天的分享就到這里。
記:
最近剛開學(xué),各種事兒,選導(dǎo)師,研一七天都有課,導(dǎo)致時(shí)間緊張,希望自己還是可以平衡好學(xué)業(yè)和代碼的關(guān)系吧,謝謝大家支持。
整理不易,大家多多支持。
到此這篇關(guān)于Java 多線程之兩步掌握的文章就介紹到這了,更多相關(guān)Java 多線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis 參數(shù)類型為String時(shí)常見問題及解決方法
這篇文章主要介紹了MyBatis 參數(shù)類型為String時(shí)常見問題及解決方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03
基于java Springboot實(shí)現(xiàn)教務(wù)管理系統(tǒng)詳解
這篇文章主要介紹了Java 實(shí)現(xiàn)簡易教務(wù)管理系統(tǒng)的代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
ScheduledExecutorService任務(wù)定時(shí)代碼示例
這篇文章主要介紹了ScheduledExecutorService任務(wù)定時(shí)代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01
java 數(shù)據(jù)結(jié)構(gòu)中棧結(jié)構(gòu)應(yīng)用的兩個(gè)實(shí)例
這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu)中棧結(jié)構(gòu)應(yīng)用的兩個(gè)實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-06-06
mybatis中使用InsertProvider注解報(bào)錯(cuò)解決全過程
這篇文章主要介紹了mybatis中使用InsertProvider注解報(bào)錯(cuò)解決全過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Java8 Lambda表達(dá)式模板方法實(shí)現(xiàn)解析
這篇文章主要介紹了Java8 Lambda表達(dá)式模板方法實(shí)現(xiàn)解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
基于Springboot一個(gè)注解搞定數(shù)據(jù)字典的實(shí)踐方案
這篇文章主要介紹了基于Springboot一個(gè)注解搞定數(shù)據(jù)字典問題,大致的方向是自定義注解,在序列化的時(shí)候進(jìn)行數(shù)據(jù)處理,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06

