創建線程的方式
創建線程一般通過繼承Thread、實現Runnable接口、ExecutorService、Callable和Future等多種方式。
Thread實現
package all;
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("測試Thread");
}
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
myThread1.start();
MyThread myThread2 = new MyThread();
myThread2.start();
}
}
Runnable實現
package all;
public class MyRunnable implements Runnable {
private volatile int i =5;
@Override
public void run() {
while (i > 0){
System.out.println("Runnable測試");
--i;
}
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
Thread thread1 = new Thread(myRunnable);
thread1.start();
}
}
Callable和FutureTask
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class MyCallable implements Callable<String> {
public String call() throws Exception {
return "TestCallable";
}
public static void main(String[] args) throws Exception {
FutureTask<String> futureTask = new FutureTask(new MyCallable());
futureTask.run();
if(futureTask.isDone()){
System.out.println(futureTask.get());
}
}
}
線程的生命週期
1.NEW:初始狀態,線程被創建,但是還沒有調用start方法
2.RUNNABLE:運行狀態,JAVA 線程把操作系統中的就緒和運行兩種狀態統一稱爲“運行中”
3.BOLCKED:阻塞狀態,表示線程進入等待狀態,也就是線程因爲某種原因放棄了 CPU 使用權,阻塞也分爲幾種情況
-->等待阻塞:運行的線程執行 wait 方法,jvm 會把當前線程放入到等待隊列
-->同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被其他線程鎖佔用了,那麼jvm會把當前的線程放入到鎖池中
-->其他阻塞:運行的線程執行 Thread.sleep 或者 t.join 方法,或者發出了I/O請求時,JVM 會把當前線程設置爲阻塞狀態,當 sleep 結束、join線程終止、io處理完畢則線程恢復
4.TIME_WAITING:超時等待狀態,超時以後自動返回
5.TERMINATED:終止狀態,表示當前線程執行完畢
代碼演示:
import java.util.concurrent.TimeUnit;
public class ThreadTest {
public static void main(String[] args) {
//TIME_WAITING
new Thread(()->{
while (true){
try {
TimeUnit.SECONDS.sleep(1000);
}catch (InterruptedException ex){
ex.fillInStackTrace();
}
}
},"測試Time_Waiting").start();
//WAITING
new Thread(()->{
while (true){
synchronized (ThreadTest.class){
try {
ThreadTest.class.wait();
}catch (InterruptedException ex){
ex.fillInStackTrace();
}
}
}
},"測試WAITING").start();
new Thread(new BlockedDemo(),"BlockedDemo01").start();
new Thread(new BlockedDemo(),"BlockedDemo02").start();
}
}
class BlockedDemo extends Thread{
public void run(){
synchronized (BlockedDemo.class){
while(true){
try {
TimeUnit. SECONDS .sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
通過idea終端輸入jps查到線程進程,使用jstack 進程查看堆棧信息
終止線程(Interrupt)
其它線程通過調用當前線程的Thread.Interrupt()方法向當前線程發送一個信號,告訴它可以中斷線程的執行了,至於什麼時候中斷,取決於當前線程自己。
判斷線程是否中斷可以使用Thread.currentThread().isInterrupted()來實現。
下面是一個例子:
package all;
public class ThreadTest {
private static int i;
public static void main(String[] args) throws Exception {
Thread thread = new Thread(()->{
//默認狀態是false,
while(!Thread.currentThread().isInterrupted()){
i++;
}
System.out.println("Num:"+i+",isInterrupted:"+Thread.currentThread().isInterrupted());
},"測試線程");
thread.start();
Thread.sleep(1);
//發送中斷標識,isInterrupted賦值true
thread.interrupt(); //可以註釋掉試試效果
}
}
執行結果:
Num:7124,isInterrupted:true
Process finished with exit code 0
線程的復位
線程的中斷就是通過Thread.Interrupt()方法把isTerrupted的值從false改爲true,但是中斷把值改爲true以後什麼也沒有做,是不是應該恢復到原始值呢,恢復原始值就是線程的復位通過Thread.interrupted,這只是我的個人理解。
通過同一個代碼對比一下:
package all;
public class ThreadTest {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(()->{
while(true){
//初始值是flase
if(Thread.currentThread().isInterrupted()){
System.out.println("復位前的值:"+Thread.currentThread().isInterrupted());
Thread.interrupted();
System.out.println("復位後的值:"+Thread.currentThread().isInterrupted());
}
}
},"測試線程Thread.interrupted()");
thread.start();
Thread.sleep(1);
thread.interrupt(); //發送中斷標識賦值爲true
}
}
執行結果:
復位前的值:true
復位後的值:false
爲什麼要復位
Thread.interrupted()是屬於當前線程的,是當前線程對外界中斷信號的一個響應,表示自己已經得到了中斷信號,
但不會立刻中斷自己,具體什麼時候中斷由自己決定,讓外界知道在自身中斷前,他的中斷狀態仍然是 false,這就
是復位的原因。
線程的啓動原理
從圖中可以看出start方法會通過JVM創建一個線程,然後回調run方法,等於是主線程和新創建的線程一起執行。
run方法沒有創建怎麼線程只有一個主線程等於同步執行。