對象及變量的併發訪問
1 synchronized同步方法
1.3 方法內部的變量都是不存在線程安全問題,永遠都是線程安全的,因爲方法內部的變量是私有的特性造成的。
1.4 實例變量就會存在線程安全問題
package thread;
public class HaselfPrivateNum {
private int num = 0 ;
public void addI(String username) throws InterruptedException {
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);
}
}
package thread;
public class ThreadA extends Thread {
private HaselfPrivateNum numref;
public ThreadA(HaselfPrivateNum numref) {
super();
this.numref = numref;
}
@Override
public void run() {
super.run();
try {
numref.addI("a");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package thread;
public class ThreadB extends Thread {
private HaselfPrivateNum numref;
public ThreadB(HaselfPrivateNum numref) {
super();
this.numref = numref;
}
@Override
public void run() {
super.run();
try {
numref.addI("b");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package thread;
public class Run {
public static void main(String[] args) {
HaselfPrivateNum haselfPrivateNum = new HaselfPrivateNum();
ThreadA A = new ThreadA(haselfPrivateNum);
A.start();
ThreadB B = new ThreadB(haselfPrivateNum);
B.start();
}
}
結果展示
a set over
b set over
b num = 200
a num = 200
添加一個sycnhroized()同步方法
package thread;
public class HaselfPrivateNum {
private int num = 0 ;
synchronized public void addI(String username) throws InterruptedException {
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);
}
}
主方法
package thread;
public class Run {
public static void main(String[] args) {
HaselfPrivateNum haselfPrivateNum = new HaselfPrivateNum();
ThreadA A = new ThreadA(haselfPrivateNum);
A.start();
ThreadB B = new ThreadB(haselfPrivateNum);
B.start();
}
}
結果展示:
b set over
b num = 200
a set over
a num = 100
關鍵字synchronized取得的鎖都是對象鎖,而不是把一小段代碼方法當做鎖,所以線程先執行那個帶synchronized關鍵字的方法,就先獲得哪一個對象鎖,由於new 了兩個實例對象,所以有兩個鎖。只有共享資源的讀寫訪問才需要同步化。aynchronized是異步鎖。
1.5 髒讀,synchronized可重入鎖,異常線程鎖自動釋放,同步不具有繼承性
發生髒讀的情況,就是取值時,實例變量已經被其他線程修改了,加上synchronized方法就可以解決.就是當第一次調用對象鎖時,在其內部在次調用,是可以再次獲取鎖的,雖然第一次的鎖沒有解開。不然會造成死鎖問題
1.6同步代碼塊
同步代碼塊,當一個線程正在訪問一個Object中的同步代碼塊時候,另外一個線程可以訪問Object中非同步的代碼塊,可以一半異步,一半同步·,當線程訪問一個Object同步代碼塊時,將停止訪問這個object類中的另外一個同步代碼塊
package threadMainTest;
public class ObjectService {
public void serviceMethodA() {
synchronized(this) {
System.out.println("A start time is " + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("A end time is " + System.currentTimeMillis());
}
}
public void serviceMethodB() {
synchronized(this) {
System.out.println("B start time is " + System.currentTimeMillis());
System.out.println("B end time is " + System.currentTimeMillis());
}
}
}
package threadMainTest;
import org.omg.PortableInterceptor.ObjectReferenceTemplateSeqHolder;
public class ThreadA extends Thread {
private ObjectService objectService;
public ThreadA(ObjectService objectService) {
super();
this.objectService = objectService;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
objectService.serviceMethodA();
}
}
package threadMainTest;
import org.omg.PortableInterceptor.ObjectReferenceTemplateSeqHolder;
public class ThreadB extends Thread {
private ObjectService objectService;
public ThreadB(ObjectService objectService) {
super();
this.objectService = objectService;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
objectService.serviceMethodB();
}
}
package threadMainTest;
public class Run2 {
public static void main(String[] args) throws InterruptedException {
ObjectService objectService = new ObjectService();
ThreadA threadA = new ThreadA(objectService);
threadA.start();
//Thread.sleep(200);
ThreadB threadB = new ThreadB(objectService);
threadB.start();
}
}
運行結果:
A start time is 1543307979305
A end time is 1543307981313
B start time is 1543307981313
B end time is 1543307981313
注:當多個線程調用同一個對象中的Synchroinzed同步方法或Synchroized(this)同步代碼塊的時,調用的效果是安順序執行,也就是同步的,阻塞的。
1.7 靜態同步Synchronized
當synchronized加到Static方法上,是給Class類上鎖,加到非Static方法上,是給對象上鎖。String常量池對Synchronized的影響。
package threadMainTest;
public class ObjectService {
public static void print(String username) {
synchronized (username) {
while(true) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
package threadMainTest;
import org.omg.PortableInterceptor.ObjectReferenceTemplateSeqHolder;
public class ThreadA extends Thread {
private ObjectService objectService;
public ThreadA(ObjectService objectService) {
super();
this.objectService = objectService;
}
@Override
public void run() {
objectService.print("AA");
}
}
package threadMainTest;
import org.omg.PortableInterceptor.ObjectReferenceTemplateSeqHolder;
public class ThreadB extends Thread {
private ObjectService objectService;
public ThreadB(ObjectService objectService) {
super();
this.objectService = objectService;
}
@Override
public void run() {
objectService.print("AA");
}
}
package threadMainTest;
public class Run2 {
public static void main(String[] args) throws InterruptedException {
ObjectService objectService = new ObjectService();
ThreadA threadA = new ThreadA(objectService);
threadA.setName("A");
threadA.start();
//Thread.sleep(200);
ThreadB threadB = new ThreadB(objectService);
threadB.setName("B");
threadB.start();
}
}
結果展示
A
A
A
A
A
A
A
A
A
A
A
因爲兩個線程都是裏面的輸入字符都是AA,所以會導致,兩個線程拿到的鎖都是相同的,所以會造成B阻塞.這是String常量池引起的,這種情況,可以把變量改爲Object類型,這樣就不是一個鎖了,因爲new Objectr()不在緩存裏面。所以第二次會new 一個新的對象。
1.8 死鎖就是雙方互相持有對方鎖的情況,比如A執行需要用到B對象,但是B執行也需要用到A對象。同時需要,則會造成死鎖,切記,死鎖和死循環是兩種不同的情況,死循環是不斷地重複,死鎖是等待對方釋放資源。
2. Volatile關鍵字
Volatile變量增加了實例變量在多個線程的可見性。但volatile關鍵字最致命的是不支持原子性,volatile只能修飾變量,synchronized可以保證原子性,也可以間接保證可見性,因爲它會將私有內存和公共內存中的數據做同步。
package thread;
public class HaselfPrivateNum extends Thread{
volatile public static int num ;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
num++;
}
System.out.println("num = " + num);
}
}
package thread;
public class Run {
public static void main(String[] args) {
HaselfPrivateNum []haselfPrivateNum = new HaselfPrivateNum[100];
for (int i = 0; i < 100; i++) {
haselfPrivateNum[i] = new HaselfPrivateNum();
}
for (int i = 0; i < 100; i++) {
haselfPrivateNum[i].start();
}
}
}
結果展示:
num = 3600
num = 3900
num = 4000
num = 4100
num = 4200
num = 4300
num = 4400
num = 4500
num = 4600
增加了線程之間的可見性,但是不具備同步性!關鍵字valitile保證了線程每次從共享內存中讀取數據,而不是從私有內存中讀取數據,這樣保證了同步數據的可見性