主線程幾乎總是比子線程要早執行,但是多線程的執行是沒有固定順序的,誰先搶到資源就先執行誰
在虛擬機上,其實主線程mt.run()幾乎總是會比.start()調用的run()要早執行,因爲主線程Thread.start()在調用(caller)線程上創建好線程就返回了,緊接着就可以去調用執行tt.m2();而在被調用(callee)的新線程上還要經過一些JVM內部的初始化動作才能跑到指定的入口方法。
輸出結果 (一種可能)
****開始測試********
****中間分割********
****結束分隔符*******
****第一個線程********
ab
12
****第二個線程********
abcd
1234
Process finished with exit code 0
代碼
package com.atguigu.java1;
/**
* @author liu
* @Description
*/
public class ThreadTest1 {
public static void main(String[] args) {
StringBuffer s2 = new StringBuffer();
StringBuffer s1 = new StringBuffer();
System.out.println("****開始測試********");
new Thread() {
@Override
public void run() {
System.out.println("****第一個線程********");
synchronized (s1){
s1.append("a");
s2.append("1");
//try是爲了測試死鎖
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
synchronized (s2) {
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
//中間
System.out.println("****中間分割********");
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("****第二個線程********");
synchronized (s2) {
s1.append("c");
s2.append("3");
// try {
// Thread.sleep(0);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
synchronized (s1) {
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
//
System.out.println("****結束分隔符*******");
}
}
解決線程安全的兩種方式
方式一:同步代碼塊
synchronized(同步監視器){
//需要被同步的代碼
}
說明:1.操作共享數據的代碼,即爲需要被同步的代碼
2.共享數據:多個線程共同操作的變量,比如:ticket就是共享數據。
3.同步監視器:俗稱,鎖。任何一個類的對象,都可以充當鎖。要求,多個線程必須要共用同一個鎖。
方式二:同步方法(public synchronized void show() )
如果操作共享數據的代碼完整的聲明在一個方法中,我們不妨礙將此方法聲明在同步中。
1.同步方法仍然涉及到同步監視器,知識不需要我們顯示的聲明
2.非靜態的同步方法,同步監視器是:this
靜態的同步方法,同步監視器是:當前類本身(類.class)