JAVA線程同步

**

1. 什麼是線程同步?

**
當多個線程共同訪問同一資源時,會引發問題,所以需要線程同步來保證對資源的訪問有序進行

例如:

class Toilet implements Runnable{
    //記錄可用的廁所數目
    private int toilet = 1;
    public void run() {
        if(toilet>0) {
            toilet--;
            System.out.println(Thread.currentThread().getName()+"在廁所,別人不要進來");
        }
        toilet++;
        System.out.println(Thread.currentThread().getName()+"從廁所出來了,別人可以進來");
    }
}

public class Hello{
    public static void main(String []args) {
        Toilet t = new Toilet();
        for(int i =0;i<10;i++) {
            new Thread(t,i+"").start();
        }
    }
}

輸出:

0在廁所,別人不要進來
0從廁所出來了,別人可以進來
1在廁所,別人不要進來
1從廁所出來了,別人可以進來
3在廁所,別人不要進來
3從廁所出來了,別人可以進來
2在廁所,別人不要進來
2從廁所出來了,別人可以進來
4在廁所,別人不要進來
4從廁所出來了,別人可以進來
5在廁所,別人不要進來
5從廁所出來了,別人可以進來
6在廁所,別人不要進來            //6還沒出來,7就進去了,這顯然不合理
7在廁所,別人不要進來
7從廁所出來了,別人可以進來
6從廁所出來了,別人可以進來
9在廁所,別人不要進來
8在廁所,別人不要進來
8從廁所出來了,別人可以進來
9從廁所出來了,別人可以進來

以上顯然不合理,線程對資源的使用很隨意,當多個線程對同一資源進行修改的時候,會照成混亂。

2. JAVA中線程同步的方式

1. 採用synchronized關鍵字定義代碼塊

synchronized (obj){

代碼
}
其中obj是同步監視器,一般將可能被多個線程使用的共享資源充當同步監視器,任何時刻只可能有一個線程能夠訪問同步監視器,當代碼執行完之後纔會釋放同步監視器。

以下情況會釋放同步監視器

  • 同步方法、同步代碼塊執行完畢
  • 同步方法、同步代碼中遇到return、break
  • 出現Error、Exception導致異常結束
  • 當前程序執行了同步監視器的wait方法

以下情況不會釋放同步監視器

  • 當前程序調用Thread.sleep、Thread.yield方法,暫停當前方法的執行
  • 其他線程調用當前線程的suspend方法將當前線程掛起
class Toilet implements Runnable{
    //記錄可用的廁所數目
    private int toilet = 1;
    public void run() {
        synchronized(this) {    //當前的對象
            if(toilet>0) {
                toilet--;
                System.out.println(Thread.currentThread().getName()+"在廁所,別人不要進來");
            }
            toilet++;
            System.out.println(Thread.currentThread().getName()+"從廁所出來了,別人可以進來");
        }
    }
}
public class Hello{
    public static void main(String []args) {
        Toilet t = new Toilet();
        for(int i =0;i<10;i++) {
            new Thread(t,i+"").start();
        }
    }
}

輸出:

0在廁所,別人不要進來
0從廁所出來了,別人可以進來
1在廁所,別人不要進來
1從廁所出來了,別人可以進來
2在廁所,別人不要進來
2從廁所出來了,別人可以進來
3在廁所,別人不要進來
3從廁所出來了,別人可以進來
4在廁所,別人不要進來
4從廁所出來了,別人可以進來
5在廁所,別人不要進來
5從廁所出來了,別人可以進來
6在廁所,別人不要進來
6從廁所出來了,別人可以進來
8在廁所,別人不要進來
8從廁所出來了,別人可以進來
7在廁所,別人不要進來
7從廁所出來了,別人可以進來
9在廁所,別人不要進來
9從廁所出來了,別人可以進來

一切正常了

2.採用synchronized關鍵字定義方法

class Toilet implements Runnable{
    //記錄可用的廁所數目
    private int toilet = 1;
    public synchronized void run() {    //synchronized應該放在void之前
        if(toilet>0) {
            toilet--;
            System.out.println(Thread.currentThread().getName()+"在廁所,別人不要進來");
        }
        toilet++;
        System.out.println(Thread.currentThread().getName()+"從廁所出來了,別人可以進來");
    }

}
public class Hello{
    public static void main(String []args) {
        Toilet t = new Toilet();
        for(int i =0;i<10;i++) {
            new Thread(t,i+"").start();
        }
    }
}

輸出:

0在廁所,別人不要進來
0從廁所出來了,別人可以進來
3在廁所,別人不要進來
3從廁所出來了,別人可以進來
1在廁所,別人不要進來
1從廁所出來了,別人可以進來
2在廁所,別人不要進來
2從廁所出來了,別人可以進來
4在廁所,別人不要進來
4從廁所出來了,別人可以進來
5在廁所,別人不要進來
5從廁所出來了,別人可以進來
8在廁所,別人不要進來
8從廁所出來了,別人可以進來
6在廁所,別人不要進來
6從廁所出來了,別人可以進來
9在廁所,別人不要進來
9從廁所出來了,別人可以進來
7在廁所,別人不要進來
7從廁所出來了,別人可以進來

一切也正常。

3.採用同步鎖Lock來控制線程同步

Lock是JAVA.util.concurrent.locks裏的接口
一般使用ReentrantLock(可重入鎖)類。
例如:

class Toilet implements Runnable{
    private final ReentrantLock lock = new ReentrantLock();
    public void run() {
        lock.lock();      //上鎖
        try {
            System.out.println(Thread.currentThread().getName()+"在廁所,別人不要進來");
            System.out.println(Thread.currentThread().getName()+"出來了,別人可以進去");
        } finally {
            lock.unlock();   //釋放鎖
        }
    }

}
public class Hello{
    public static void main(String []args) {
        Toilet t = new Toilet();
        for(int i =0;i<10;i++) {
            new Thread(t,i+"").start();
        }
    }
}

使用線程同步會使程序運行速度變慢,所以只有需要同步的地方纔去使用同步

發佈了43 篇原創文章 · 獲贊 9 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章