- 如何開啓線程
開啓線程2種方式:
自定義類繼承Thread類,重寫run方法;new 自定義類().start()
自定義類實現Runnable接口,實現run方法;new Thread(new 自定義類()).start()
- Thread和Runable
Thread是對線程的抽象封裝,完成獨立完成某個任務的調度,包括任務的開始、暫停、結束等操作。本身是實現Runnable接口,所以具有Runnable的所有功能。
Runnable 是對任務的抽象封裝,業務任務的具體邏輯封裝。本身不具有獨立資源調度。需要Thread協助完成,這也就是前面說的開啓線程需要先new Thread() 通過參數把Runnable傳遞進去。
- JVM開啓,默認開啓哪些線程(JDK1.8爲例)
public static void main(String[] args) {
//虛擬機線程系統的管理接口(JVM開啓的線程);對JVM系統進行監控時,使用類ManagementFactory
ThreadMXBean threadMXBean= ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for(ThreadInfo threadInfo:threadInfos){
System.out.println("["+threadInfo.getThreadId()+"]"+threadInfo.getThreadName());
}
/* 一般項目啓動會啓動的線程有:
* [8]JDWP Command Reader
[7]JDWP Event Helper Thread
[6]JDWP Transport Listener: dt_socket
[5]Attach Listener
[4]Signal Dispatcher
[3]Finalizer 線程管理Object finalize()方法,每個對象銷燬時都會調用這個方法;不推薦在這個方法中寫資源釋放,有時調用不到(main結束這個線程也就沒有了)
[2]Reference Handler
[1]main 主線程,線程關閉其他線程會依次關閉
* */
}
- stop/interrupt/sleep等方法總結
stop():結束線程,比較野蠻會立即結束線程。比如當前線程正在寫一個文件,或者正在寫入數據庫,正在完成一半任務時,接到stop命令,立即線程結束,導致文件不完整,錯誤的文件或數據庫數據不完整等問題。所以JDK1.8之後不在推薦使用。當然如果能夠保證當前線程沒有這些類似任務,採用stop停止線程也是可以的。
interrupt():標記線程狀態爲中斷狀態(線程本身並不會結束),此時線程中如果有join、wait、sleep等方法調用的時候會拋出異常java.lang.InterruptedException。在線程的run()方法中我們需要自己寫邏輯結束線程。isInterrupt()可獲取線程的中斷狀態。Thread.interrupt()靜態方法內部會調用當前線程的isinterrupt()方法,但是內部也會再次把狀態標誌位修改,所有一般都是用實例interrupt方法。對於Runnable實例化的線程,一般用Thread.currentThread().interrupt() 方法判斷。
sleep():使當前線程進入阻塞狀態。同時這個方法內部會監測interrupt狀態,如果當前線程中斷後,同時線程還睡眠時,會報異常java.lang.InterruptedException: sleep interrupted。sleep()並不會釋放對象鎖,只有鎖代碼塊執行完後,其他線程才能夠獲取鎖。
- 線程stop方法案例
import java.util.concurrent.TimeUnit;
/**
* @ClassName xu01
* @Description des
* @Author zyk
* @Date 2019/9/24
* @Version 1.0
**/
public class xu01 {
private static class UseThread extends Thread{
@Override
public void run() {
while(true) {
System.out.println("正在寫入文件,寫入部分...");
}
}
}
public static void main(String[] args) {
UseThread useThread=new UseThread();
useThread.start();
//main線程睡眠20
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//main線程2秒後關閉
// 1:stop() 是一個@Deprecated不推薦使用,過於野蠻停止線程(會出現線程寫部分文件就掛了)
useThread.stop();
System.out.println("main 線程結束");
}
}
- 線程interrupt方法案例
import java.util.concurrent.TimeUnit;
/**
* @ClassName xu01
* @Description des
* @Author zyk
* @Date 2019/9/24
* @Version 1.0
**/
public class xu01 {
private static class UseThread extends Thread{
@Override
public void run() {
while(true) {
System.out.println("正在寫入文件,寫入部分...");
//判斷線程狀態是否,是否需要結束
// if(isInterrupted()){
//Thread.interrupted靜態方法也是同樣的功能,內部會獲取當前線程
if(Thread.interrupted()){
System.out.println("線程狀態:"+interrupted());
System.out.println("判斷當前文件寫完了,然後結束線程,不會導致文件是個損壞的文件");
break;
}
}
}
}
public static void main(String[] args) {
UseThread useThread=new UseThread();
useThread.start();
//main線程睡眠20
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//main線程2秒後關閉
//只是標記線程標誌位爲中斷狀態,需要在run方法中寫邏輯結束線程,否則線程還是不能夠結束
useThread.interrupt();
System.out.println("main 線程結束");
}
}
- 線程sleep方法案例
在線程中添加sleep睡眠邏輯,做完一件事後睡眠一段時間,再次做事情。
import java.util.concurrent.TimeUnit;
/**
* @ClassName xu01
* @Description des
* @Author zyk
* @Date 2019/9/24
* @Version 1.0
**/
public class xu01 {
private static class UseThread extends Thread{
@Override
public void run() {
while(true) {
System.out.println("正在做某件任務...");
System.out.println("結束一段任務後,線程睡眠一段時間");
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(Thread.interrupted()){
System.out.println("線程狀態:"+interrupted());
System.out.println("判斷當前文件寫完了,然後結束線程,不會導致文件是個損壞的文件");
break;
}
}
}
}
public static void main(String[] args) {
UseThread useThread=new UseThread();
useThread.start();
//main線程睡眠20
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//main線程2秒後關閉
//只是標記線程標誌位爲中斷狀態,需要在run方法中寫邏輯結束線程,否則線程還是不能夠結束
useThread.interrupt();
System.out.println("main 線程結束");
}
}
注意:這樣線程並不會結束,運行結果如下
正在做某件任務...
結束一段任務後,線程睡眠一段時間
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at controller.multithread.xu01$UseThread.run(xu01.java:21)
main 線程結束
正在做某件任務...
結束一段任務後,線程睡眠一段時間
正在做某件任務...
結束一段任務後,線程睡眠一段時間
這是因爲 TimeUnit.MILLISECONDS.sleep(20);會拋出異常後,把當前線程中斷狀態又改回去了,導致線程還會繼續執行。
需要修改代碼:第一種方式,在sleep方法上添加try-catch,在InterruptedException異常代碼塊中再次調用interrupte()方法。第二種方式,把while死循環監視起來,出現InterruptedException 異常後結束線程,修改後代碼如下:
import java.util.concurrent.TimeUnit;
/**
* @ClassName xu01
* @Description des
* @Author zyk
* @Date 2019/9/24
* @Version 1.0
**/
public class xu01 {
private static class UseThread extends Thread{
@Override
public void run() {
try {
while (true) {
System.out.println("正在做某件任務...");
System.out.println("結束一段任務後,線程睡眠一段時間");
try {
TimeUnit.MILLISECONDS.sleep(20);
//java.lang.InterruptedException: sleep interrupted 需要把這個異常拋出去
} catch (InterruptedException e) {
//interrupt();
throw e;
}
if (Thread.interrupted()) {
System.out.println("線程狀態:" + interrupted());
System.out.println("判斷當前文件寫完了,然後結束線程,不會導致文件是個損壞的文件");
break;
}
}
}catch (InterruptedException e){
e.printStackTrace();
System.out.println("子線程結束");
}
}
}
public static void main(String[] args) {
UseThread useThread=new UseThread();
useThread.start();
//main線程睡眠20
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//main線程2秒後關閉
//只是標記線程標誌位爲中斷狀態,需要在run方法中寫邏輯結束線程,否則線程還是不能夠結束
useThread.interrupt();
System.out.println("main 線程結束");
}
}
- 線程池使用
JDK1.8不在推薦使用Executors創建線程了,推薦使用ThreadPoolExecutor,可控制最大線程數量。而Executors 最多創建線程數量是 Integer.MAX_VALUE 會導致系統OOM問題。
public static void main(String[] args) throws ExecutionException, InterruptedException {
int count=230;
//線程池1:
ExecutorService pool1=Executors.newCachedThreadPool();
ExecutorService pool2=Executors.newFixedThreadPool(5);
//線程池2:
ThreadFactory namedThreadFactory=new ThreadFactoryBuilder()
.setNameFormat("jake-pool-%d")
.build();
ExecutorService pool=new ThreadPoolExecutor(5,200,
60L,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(1024),
namedThreadFactory,new ThreadPoolExecutor.AbortPolicy());
for(int i=0;i<count;i++){
pool.execute(()->{
System.out.println("========線程名稱:"+Thread.currentThread().getName());
});
}
pool.shutdown();
System.out.println("shutdown");
}