java多執行緒–同步鎖

西北野狼發表於2017-03-30

為了解決多執行緒安全問題
在 Java 5.0 之前,協調共享物件的訪問時可以使用的機制只有 synchronized 和 volatile 。
Java 5.0 後增加了一些新的機制,但並不是一種替代內建鎖的方法,而是當內建鎖不適用時,作為一種可選擇的高階功能。

隱式鎖

synchronized

  1. 同步程式碼塊;
  2. 同步方法。

顯示鎖 Lock

jdk 1.5 後:同步鎖 Lock 需要通過 lock() 方法上鎖,必須通過 unlock() 方法進行釋放鎖。
ReentrantLock 實現了 Lock 介面,並提供了與synchronized 相同的互斥性和記憶體可見性。但相較於synchronized 提供了更高的處理鎖的靈活性。

Lock使用方式:

Lock lock = new ReentrantLock();
lock.lock();
try {
} finally {
lock.unlock();
}

相關API:

  1. void lock() 獲取鎖。
  2. void lockInterruptibly() 如果當前執行緒未被中斷,則獲取鎖。
  3. Condition newCondition() 返回繫結到此 Lock 例項的新 Condition 例項。
  4. boolean tryLock() 僅在呼叫時鎖為空閒狀態才獲取該鎖。
  5. boolean tryLock(long time, TimeUnit unit) 如果鎖在給定的等待時間內空閒,並且當前執行緒未被中斷,則獲取鎖。
  6. void unlock()釋放鎖。

不使用鎖出現多執行緒安全問題demo:

package com.company;

public class TestLock {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(ticket, "1號視窗").start();
        new Thread(ticket, "2號視窗").start();
        new Thread(ticket, "3號視窗").start();
    }

}

class Ticket implements Runnable{

    private int tick = 100;


    @Override
    public void run() {
        while(true){
            if(tick > 0){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                }

                System.out.println(Thread.currentThread().getName() + " 完成售票,餘票為:" + --tick);
            }

        }
    }

}

看出現的問題(擷取結果中一部分):

3號視窗 完成售票,餘票為:10
1號視窗 完成售票,餘票為:8
2號視窗 完成售票,餘票為:8
3號視窗 完成售票,餘票為:8
2號視窗 完成售票,餘票為:7
1號視窗 完成售票,餘票為:7
3號視窗 完成售票,餘票為:7
1號視窗 完成售票,餘票為:6
2號視窗 完成售票,餘票為:5
3號視窗 完成售票,餘票為:4
3號視窗 完成售票,餘票為:3
1號視窗 完成售票,餘票為:3
2號視窗 完成售票,餘票為:3
3號視窗 完成售票,餘票為:2
1號視窗 完成售票,餘票為:0
2號視窗 完成售票,餘票為:1

現在我們用Lock來進行處理:

package com.company;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestLock {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(ticket, "1號視窗").start();
        new Thread(ticket, "2號視窗").start();
        new Thread(ticket, "3號視窗").start();
    }

}

class Ticket implements Runnable {

    private int tick = 100;
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (tick > 0) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                    }

                    System.out.println(Thread.currentThread().getName() + " 完成售票,餘票為:" + --tick);
                }
            } finally {
                lock.unlock();
            }


        }
    }

}

看下結果(當然也是一部分),很完美解決多執行緒安全問題:

2號視窗 完成售票,餘票為:20
2號視窗 完成售票,餘票為:19
2號視窗 完成售票,餘票為:18
2號視窗 完成售票,餘票為:17
2號視窗 完成售票,餘票為:16
2號視窗 完成售票,餘票為:15
2號視窗 完成售票,餘票為:14
2號視窗 完成售票,餘票為:13
2號視窗 完成售票,餘票為:12
2號視窗 完成售票,餘票為:11
2號視窗 完成售票,餘票為:10
2號視窗 完成售票,餘票為:9
2號視窗 完成售票,餘票為:8
2號視窗 完成售票,餘票為:7
2號視窗 完成售票,餘票為:6
2號視窗 完成售票,餘票為:5
2號視窗 完成售票,餘票為:4
2號視窗 完成售票,餘票為:3
2號視窗 完成售票,餘票為:2
2號視窗 完成售票,餘票為:1
2號視窗 完成售票,餘票為:0

 


相關文章