1 線程安全
在單線程程序中,每次只能做一件事情,後面的事情需要等待前面的事情完成後纔可以進行,但是如果使用多線程程序,就會發生多個線程搶佔資源的問題,從而產生線程安全問題。實質上線程安全問題來源於多個線程在操作共享的數據,並且操作共享數據的代碼有多條,即當一個線程在執行操作共享數據的多條代碼過程中,其他線程參與了運算。解決思路:將多條操作共享數據的線程代碼封裝起來,當有線程在執行這些代碼的時候,其他線程是不可以參與運算的,必須要當前線程把這些代碼都執行完畢後,其他線程纔可以參與運算。Java提供了線程同步的機制來防止資源訪問的衝突,同步機制使用synchronized關鍵字。同步的好處:解決了線程安全問題。同步的弊端:相對地降低了效率,因爲同步外的線程都會判斷同步鎖。
2 同步塊
同步塊也被稱爲臨界區,它使用synchronized關鍵字建立,語法格式爲:synchronized(object){需要被同步的代碼;}。通常是將共享資源的操作代碼放置在synchronized定義的區域內,這樣當其他線程也獲取到這個鎖時,必須等待鎖被釋放時才能進入該區域。object爲任意一個對象,每個對象都存在一個標誌位,並具有兩個值,分別爲0和1。一個線程運行到同步塊時,首先檢查該對象的標誌位,如果爲0狀態,表明此同步塊中存在其他線程在運行。這時該線程處於就緒狀態,直到處於同步塊中的線程執行完同步塊中的代碼爲止。這時該對象的標誌位被設置爲1,該線程才能執行同步塊中的代碼,並將object對象的標誌位設置爲0,防止其他線程執行同步塊中的代碼。
同步塊程序示例:
package captain;
public class ThreadSafeDemo implements Runnable{
int num = 10;
Object obj = new Object();//同步鎖對象obj必須作爲類的成員,才能保證鎖的唯一性。
public void run(){
//Object obj = new Object();//假如在run()方法內創建同步鎖對象obj,則並不能保證線程同步。
while(true){
//同步塊
synchronized(obj){//obj對象爲同步鎖
if(num > 0){
/*
* 線程休眠會拋出中斷異常,但由於Runnable接口中的run()方法沒有聲明拋出該異常,
* 故實現接口的類中的run()方法也不能聲明拋出該異常,因此只能在方法內try-catch捕捉異常。
* */
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"..."+num--);
}
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadSafeDemo tsd = new ThreadSafeDemo();
Thread t1 = new Thread(tsd);
Thread t2 = new Thread(tsd);
Thread t3 = new Thread(tsd);
Thread t4 = new Thread(tsd);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
3 同步方法
同步方法就是在方法前面修飾synchronized關鍵字的方法。當某個對象調用了同步方法時,該對象上的其他同步方法必須等待該同步方法執行完畢後才能被執行。必須將每個能訪問共享資源的方法修飾爲synchronized,否則就會出錯。同步方法,與同步塊的效果一樣,不同的是同步塊的鎖是任意一個對象,而同步方法的鎖其實是this。如果是靜態同步方法,則其鎖是方法所屬類的字節碼對象this.getClass()或者用類的靜態屬性 類名.class 來獲取。
同步方法程序示例:
package captain;
public class ThreadSafeDemo2 implements Runnable{
int num = 10;
/*同步方法,與同步塊的效果一樣,不同的是同步塊的鎖是任意一個對象,而同步方法的鎖其實是this。
*如果是靜態同步方法,則其鎖是方法所屬類的字節碼對象this.getClass()或者用類的靜態屬性 類名.class 來獲取。
* */
public synchronized void doit(){
if(num > 0){
/*
* 線程休眠會拋出中斷異常,但由於Runnable接口中的run()方法沒有聲明拋出該異常,
* 故實現接口的類中的run()方法也不能聲明拋出該異常,因此只能在方法內try-catch捕捉異常。
* */
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"..."+num--);
}
}
public void run(){
while(true){
doit();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadSafeDemo2 tsd = new ThreadSafeDemo2();
Thread t1 = new Thread(tsd);
Thread t2 = new Thread(tsd);
Thread t3 = new Thread(tsd);
Thread t4 = new Thread(tsd);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
4 線程死鎖
死鎖程序示例:
package captain;
//線程死鎖示例。兩個同步塊(或者同步方法)相互嵌套可以導致線程死鎖。
class DeadLock implements Runnable{
private boolean flag;//布爾變量使不同的線程執行不同的線程任務
DeadLock(boolean flag){
this.flag = flag;
}
//創建兩個不同的同步鎖對象。
Object obj1 = new Object();
Object obj2 = new Object();
public void run(){
if(flag){
synchronized(obj1){
System.out.println(Thread.currentThread().getName()+"..if..obj1");
synchronized(obj2){
System.out.println(Thread.currentThread().getName()+"..if..obj2");
}
}
}
else{
synchronized(obj2){
System.out.println(Thread.currentThread().getName()+"..else..obj2");
synchronized(obj1){
System.out.println(Thread.currentThread().getName()+"..else..obj1");
}
}
}
}
}
public class DeadLockDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
DeadLock dl1 = new DeadLock(true);
DeadLock dl2 = new DeadLock(false);
Thread t1 = new Thread(dl1);
Thread t2 = new Thread(dl2);
t1.start();
t2.start();
}
}