目錄
一、什麼是多線程
點擊——》進程與線程的聯繫區別
二、多線程創建的幾種方式
1、繼承Thread
thread是程序執行的一個線程,Java虛擬機允許一個應用程序多線程同時執行,每個線程都有優先級,優先級高的比優先級低的線程優先執行,每個線程有可能會被標記爲守護線程。
//繼承Thread方式,重寫run()方法 public class MyThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running"); } public static void main( String[] args ) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); thread1.start();//啓動線程thread1 thread2.start();//啓動線程thread2 } }
執行結果
Thread-1 is running
Thread-0 is running
2、實現Runnable接口
/** * 實現Runnable接口 */ public class MyThread1 implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running"); } public static void main(String[] args) { MyThread1 myThread1 = new MyThread1(); Thread thread = new Thread(myThread1); thread.start(); } }
3、實現Callable接口
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; //實現Callable<V>接口,V爲方法返回類型 public class CallableImpl implements Callable<Double> { @Override public Double call() throws Exception { Thread.sleep(1000); double result = Math.random(); System.out.println(Thread.currentThread().getName()+" is running !"); return result; } public static void main(String[] args) throws ExecutionException, InterruptedException { Callable<Double> callable = new CallableImpl(); FutureTask<Double> future = new FutureTask<>(callable); //創建並啓動線程 new Thread(future).start(); //調用get()會阻塞主線程,否則不會阻塞 Double result = future.get(); System.out.println("result=" + result); System.out.println("main is running !"); } }
4、線程池
public class ExecutorThread { public static void main(String[] args) { //使用 guava 開源框架的 ThreadFactoryBuilder 給線程池的線程設置名字 ThreadFactory nameThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build(); ExecutorService service = new ThreadPoolExecutor(4,10,0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(1024), nameThreadFactory, new ThreadPoolExecutor.AbortPolicy()); service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !")); service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !")); service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !")); service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !")); service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !")); service.shutdown(); } }
三、線程的生命週期
線程的五種基本狀態:
1)新建狀態(New):當線程對象創建後,線程進入新建狀態,如:Thread t = new Thread();
2)就緒狀態(Runnable):當調用線程對象的start()方法,即t.start(),線程就從新建狀態變爲就緒狀態。處於就緒狀態的線程只是說線程已經做好了準備,隨時等待CPU調度執行,因此調用start()方法並不是說線程立即去執行,只是線程進入了一個可運行的狀態。
3)運行狀態(Running):當處於就緒狀態的線程獲得了CPU的調度執行後,此時線程進入運行狀態。
4)阻塞狀態(Blocked):處於運行狀態中的線程由於某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被CPU調用以進入到運行狀態。根據阻塞產生的原因不同,阻塞狀態又可以分爲三種:
1.等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態;
2.同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因爲鎖被其它線程所佔用),它會進入同步阻塞狀態;
3.其他阻塞 -- 通過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
5)死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
四、Thread中的一些方法
- currentThread():返回正在執行此方法代碼的線程信息
- isAlive():判斷當前的線程是否處於活動狀態,所謂活動就是指線程已經啓動但是尚未終止。線程處於正在運行或者準備開始運行的狀態。
- sleep():讓正在執行的線程暫停執行
- getId():返回當前線程的唯一標識
/*
* Thread ID
* 線程id,在調用init初始化方法的時候,調用nextThreadID()方法生成ID
*/
private long tid;
public long getId() {
return tid;
}
/**
*同步生成線程ID方法
*/
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
- getName():獲得線程名稱
線程名稱類似“Thread-0”名稱的由來:由線程的構造方法構造而來,如下圖源碼:
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
/* For autonumbering anonymous threads.
* 自動編號的匿名線程
*/
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
- yield():放棄當前的CPU資源,讓給其他的線程去佔用CPU執行時間。
因爲放棄的時間不確定,有可能剛剛放棄,馬上又獲得CPU時間片。
- isDaemon():判斷線程是否是守護線程
五、如何停止一個線程
1、使用return停止線程:
public class Main extends Thread {
@Override
public void run() {
while (true){
if (this.isInterrupted()){
System.out.println("線程停止了!");
//使用return來停止線程
return;
}
}
}
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
main.start();
Thread.sleep(2000);
main.interrupt();
}
}
2、使用interrupt方法中斷線程:
在Thread.java類裏提供了兩種方法判斷線程是否爲停止的。
this.interrupted():測試當前線程是否已經中斷(靜態方法)。如果連續調用該方法,則第二次調用將返回false。在api文檔中說明interrupted()方法具有清除狀態的功能。執行後具有將狀態標識清除爲false的功能。
this.isInterrupted():測試線程是否已經中斷,但是不能清除狀態標識。
3、拋異常法(推薦用法):
public class MyThread4 extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 50000; i++) {
if (this.isInterrupted()) {
System.out.println( "線程已經結束,我要退出" );
// return;
throw new InterruptedException();
}
System.out.println( "i=" + (i + 1) );
}
System.out.println( "我是for下面的語句,我被執行說明線程沒有真正結束" );
} catch (InterruptedException e) {
System.out.println( "進入MyThread.java類中run方法的catch異常了" );
e.printStackTrace();
}
}
}
4、在沉睡中停止:
@Override
public void run() {
super.run();
try {
System.out.println( "begin run" );
Thread.sleep( 500 );
System.out.println( "begin end" );
} catch (InterruptedException e) {
System.out.println("在沉睡中終止");
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
MyThread5 thread5 = new MyThread5();
thread5.start();
Thread.sleep( 20 );
thread5.interrupt();
} catch (InterruptedException e) {
System.out.println( "main catch" );
e.printStackTrace();
}
}
運行結果:
從打印結果看,sleep狀態下停止某一個線程,會進入catch語句,並清除狀態值,變成false
5、爲什麼不使用stop()方法停止線程
方法stop()已經棄用,因爲如果強制讓線程終止則有可能使一些請理性的工作得不到完成。另外,對鎖的對象“解鎖”,會導致數據得不到同步的處理,出現了數據不一致的情況。
六、暫停和恢復線程
suspend()——暫停線程
resume()——恢復線程
suspend()和resume()方法缺點——獨佔、不同步
七、線程的優先級
在操作系統中,線程可以劃分優先級,優先級較高的線程得到的CPU資源較多,也就是CPU優先執行優先級較高的線程對象中的任務。
在Java中線程的優先級劃分爲1~10這10個等級。如果小於1或者大於10,則JDK拋出異常throw new IllegalArgumentException()。
JDK中使用了3個常量來預置定義優先級:
public final static int MIN_PRIORITY = 1;
public final static int MIN_PRIORITY = 5;
public final static int MIN_PRIORITY = 10;
線程優先級的特性:
1)線程的優先級具有繼承特性,比如A線程啓動B線程,則A線程與B線程的優先級是一樣的
2)線程優先級具有規則性,CPU儘量將資源讓給優先級比較高的線程
3)線程的優先級具有隨機性,換句話說優先級較高的線程不一定每一次都先執行完
八、什麼是守護線程?
在Java中線程分爲兩種:用戶線程和守護線程
什麼是守護線程?守護線程是一種特殊的線程。它的作用是爲其他線程的運行提供便利服務,最典型的應用就是GC