什麼是線程
線程是進程內的執行單元
例如 下面36417,就是進程
通過 命令,看到的,就是進程號37417裏的線程
jstack 36417
線程的狀態
線程的狀態,本來想找個圖片的,看到百度經驗這篇,說得挺好,挺詳細,就附上鍊接吧
https://jingyan.baidu.com/article/f79b7cb33886ef9144023ebf.html
線程的基本操作
創建線程的三種形式
1.直接繼承一個Thread類,重寫run方法
2.實現Runnable的接口,實現run方法,這種形式,是我平時工作中,使用最多的形式 3.創建一個Future接口的實現類 FutureTask,這種形式有返回值,並且可以拋出異常
需要注意的是,調用start方法,纔是執行線程,如果只是執行run方法,那麼程序會當成普通方法運行;
代碼如下
public class TestThread {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName());
return 10;
}
});
Thread thread = new Thread(futureTask);
Thread thread1 = new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
Thread thread2 = new Thread(runnable);
thread.start();
thread1.start();
thread2.start();
try {
thread.join();
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(futureTask.get());
}
}
線程的一些常見方法:
Thread.stop() 線程終止;不推薦使用,它會釋放所有monitor監控,本來運行中的線程,或者運行一半,任務還沒有執行完成,也會被終止
public void Thread.interrupt() // 中斷線程 public boolean Thread.isInterrupted() // 判斷是否被中斷 public static boolean Thread.interrupted() // 判斷是否被中斷,並清除當前中斷狀態
suspend() ; 掛起;suspend()不會釋放鎖,如果加鎖發生在resume()之前 ,則死鎖發生 resume();繼續執行
join(); join(long millis);等待線程結束
yeild();謙讓;是指空出cpu當前的運行週期,等待下一輪cpu的時間片
守護線程
在後臺默默地完成一些系統性的服務,比如垃圾回收線程、JIT線程就可以理解爲守護線程
當一個Java應用內,只有守護線程時,Java虛擬機就會自然退出
Thread thredTest = new Thread(() ->{
System.out.println(Thread.currentThread().getName());
}) ;
//標記爲守護線程
thredTest.setDaemon(true);
thredTest.start();
線程優先級
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
Thread high= new Thread(() ->{
System.out.println(Thread.currentThread().getName());
}) ;
Thread low= new Thread(() ->{
System.out.println(Thread.currentThread().getName());
}) ;
high.setPriority(Thread.MAX_PRIORITY);
low.setPriority(Thread.MIN_PRIORITY);
low.start();
high.start();
高優先級的線程更容易再競爭中獲勝,更容易獲得cpu的臨幸
基本的線程同步操作
synchronized
指定加鎖對象:對給定對象加鎖,進入同步代碼前要獲得給定對象的鎖。
//實例對象:同步代碼塊,鎖住的該類的實例對象
synchronized(this){
}
//class對象:鎖住的該類的類對象
synchronized(Test.class){
}
//任意實例對象:鎖住的是該創建的實例對象,下例是對lock對象加鎖
Object lock = new Obejct();
synchronized(lock){
}
對方法加鎖
//作用在方法上---實例方法:類的對象實例,鎖住的是該類的實例對象;
//相當於對當前實例加鎖,進入同步代碼前要獲得當前實例的鎖。
public synchronized void test(){
//........
}
//作用在方法上---靜態方法:鎖住的類的對象;相當於對當前類加鎖,進入同步代碼前要獲得當前類的鎖。
public static synchronized void test1(){
//.....
}
Object.wait() Obejct.notify()
首先wait方法和notify方法,是在synchronized關鍵詞中使用的,也就說,在執行方法之前,肯定是獲取到了對應的鎖操作;
舉例說明:A線程執行的時候,會釋放當前獲取到的鎖,讓出cpu的執行的時間,讓線程進入等待狀態,直到B線程獲取鎖的對象,執行對象的notify方法,纔會繼續執行;需要注意的時候,notify方法,不會釋放鎖,只有B線程執行完了,A線程纔會繼續獲取對象鎖,繼續執行
public static class T1 extends Thread{
public void run(){
synchronized (object) {
System.out.println(System.currentTimeMillis()+":T1 start! ");
try {
System.out.println(System.currentTimeMillis() +":T1 wait for object ");
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+":T1 end!");
}
}
public static class T2 extends Thread{
public void run(){
synchronized (object) {
System.out.println(System.currentTimeMillis() +":T2 start! notify one thread");
object.notify(); System.out.println(System.currentTimeMillis()+":T2 end!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
} }
}
最終的執行結果的順序,也是可以看出來的
1566523411000:T1 start!
1566523411000:T1 wait for object
1566523411000:T2 start! notify one thread
1566523411000:T2 end!
1566523411000:T1 end!
Happen-Before
程序順序原則:一個線程內保證語義的串行性
volatile規則:volatile變量的寫,先發生於讀,這保證了volatile變量的可見性
鎖規則:解鎖(unlock)必然發生在隨後的加鎖(lock)前
傳遞性:A先於B,B先於C,那麼A必然先於C
線程的start()方法先於它的每一個動作
線程的所有操作先於線程的終結(Thread.join())
線程的中斷(interrupt())先於被中斷線程的代碼
對象的構造函數執行結束先於finalize()方法