2、 synchronized同步方法

對象及變量的併發訪問
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保證了線程每次從共享內存中讀取數據,而不是從私有內存中讀取數據,這樣保證了同步數據的可見性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章