方法內部的變量爲線程安全的
“非線程安全”問題存在於“實例變量中”如果是方法內部的私有變量,則不存在“非線程安全”的問題,所得的結果也就是“線程安全”的了
實例變量非線程安全
如果多個線程共同訪問一個對象中的實例變量,則有可能出現“非線程安全”的問題。
用線程訪問的對象中如果有多個實例變量,則運行的結果可能有可能出現交叉的情況。
如果只有一個實例變量則有可能出現覆蓋的情況,看下面的測試:
創建新的項目,HasSelfPrivateNum.java的代碼如下:
public class HasSelfPrivateNum {
private int num = 0;
public void addI(String userName) {
try {
if (userName.equals("a")) {
num = 100;
System.out.println("a set over");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over");
}
System.out.println(userName + "num = " + num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ThreadA如下:
public class ThreadA extends Thread{
private HasSelfPrivateNum selfPrivateNum;
public ThreadA(HasSelfPrivateNum selfPrivateNum) {
// TODO Auto-generated constructor stub
super();
this.selfPrivateNum = selfPrivateNum;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
selfPrivateNum.addI("a");
}
}
ThreadB:
public class ThreadB extends Thread{
private HasSelfPrivateNum selfPrivateNum;
public ThreadB(HasSelfPrivateNum selfPrivateNum) {
// TODO Auto-generated constructor stub
super();
this.selfPrivateNum = selfPrivateNum;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
selfPrivateNum.addI("b");
}
}
測試代碼:
public class Test1 {
public static void main(String[] args) throws InterruptedException {
HasSelfPrivateNum hasSelfPrivateNum = new HasSelfPrivateNum();
ThreadA a = new ThreadA(hasSelfPrivateNum);
a.start();
ThreadB b = new ThreadB(hasSelfPrivateNum);
b.start();
}
}
運行結果:
由上面個的結果可得到:
如果兩個線程同時操作業務對象的實例變量,則有可能會出現非線程安全的問題,我們只需要在前面加上synchrionized字即可。更改後的代碼如下:
public class HasSelfPrivateNum {
private int num = 0;
synchronized public void addI(String userName) {
try {
if (userName.equals("a")) {
num = 100;
System.out.println("a set over");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over");
}
System.out.println(userName + "num = " + num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
再次運行代碼:
實驗結論:在兩個線程訪問同一個對象的同步方法時一定是線程安全的。
多個對象多個鎖
上面的HasSelfPrivateNum類中有同步方法addI,說明此方法應該被順序調用。
修改執行代碼如下:
public class Test1 {
public static void main(String[] args) throws InterruptedException {
HasSelfPrivateNum hasSelfPrivateNum = new HasSelfPrivateNum();
HasSelfPrivateNum hasSelfPrivateNum2 = new HasSelfPrivateNum();
ThreadA a = new ThreadA(hasSelfPrivateNum);
a.start();
ThreadB b = new ThreadB(hasSelfPrivateNum2);
b.start();
}
}
運行結果;
上面的事例是兩個線程分別訪問同一個類的兩個不同實例的相對名稱的同步方法,效果確實以異步的方式運行的,本示例由於創建了兩個業務對象,在系統中產生了兩個鎖。所以運行結果是異步的,打印的結果就是先打印b,再打印a。
明明加了synchronized關鍵字,但打印的順序卻是不是同步的,是交叉的,爲什麼是這樣的結果?
關鍵字synchronized取得的鎖都是對象鎖,而不是一段代碼或方法(函數)當做鎖,所以上面實例,那個線程先執行帶synchronized關鍵字的方法,那個線程就持有該方法所屬有的鎖Lock。其他線程只能呈現等待狀態。前提是多個線程訪問同一個對象。
如果多個線程訪問多個隊形,JVM就會創建多個鎖,上面的實例就是創建了兩個HasSelfPrivateNum 類的對象,所以就會產生兩個鎖