存储过程 锁定并发_Java并发教程–锁定:显式锁定
存儲過程 鎖定并發
1.簡介
在許多情況下,使用隱式鎖定就足夠了。 有時,我們將需要更復雜的功能。 在這種情況下, java.util.concurrent.locks包為我們提供了鎖定對象。 當涉及到內存同步時,這些鎖的內部機制與隱式鎖相同。 區別在于顯式鎖提供其他功能。
與隱式同步相比,主要優點或改進是:
- 通過讀取或寫入來分離鎖。
- 一些鎖允許并發訪問共享資源( ReadWriteLock )。
- 獲取鎖的不同方式:
- 阻塞:lock()
2.鎖對象的分類
鎖定對象實現以下兩個接口之一:
- Lock :定義鎖對象必須實現的基本功能。 基本上,這意味著獲取和釋放鎖。 與隱式鎖相反,此鎖允許以非阻塞或可中斷的方式(除阻塞方式外)獲取鎖。 主要實現:
- 重入鎖
- ReadWriteLock :它保留一對鎖,一個鎖用于只讀操作,另一個鎖用于寫操作。 可以通過不同的讀取器線程同時獲取讀取鎖(只要尚未通過寫入鎖獲取資源),而寫入鎖是排他的。 這樣,只要沒有寫操作,我們就可以讓多個線程同時讀取資源。 主要實現:
- 重入ReadWriteLock
下面的類圖顯示了不同鎖類之間的關系:
3.重入鎖
此鎖的工作方式與同步塊相同。 只要一個線程尚未被另一個線程獲取,該線程便會獲取該鎖,并且直到調用unlock之前,它才會釋放該鎖。 如果另一個線程已經獲取了該鎖,則嘗試獲取它的線程將被阻塞,直到另一個線程釋放它為止。
我們將從一個沒有鎖的簡單示例開始,然后我們將添加一個可重入鎖以查看其工作方式。
public class NoLocking {public static void main(String[] args) {Worker worker = new Worker();Thread t1 = new Thread(worker, "Thread-1");Thread t2 = new Thread(worker, "Thread-2");t1.start();t2.start();}private static class Worker implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " - 1");System.out.println(Thread.currentThread().getName() + " - 2");System.out.println(Thread.currentThread().getName() + " - 3");}} }由于上面的代碼未同步,因此線程將被交錯。 讓我們看一下輸出:
Thread-2 - 1 Thread-1 - 1 Thread-1 - 2 Thread-1 - 3 Thread-2 - 2 Thread-2 - 3現在,我們將添加一個可重入鎖,以序列化對run方法的訪問:
public class ReentrantLockExample {public static void main(String[] args) {Worker worker = new Worker();Thread t1 = new Thread(worker, "Thread-1");Thread t2 = new Thread(worker, "Thread-2");t1.start();t2.start();}private static class Worker implements Runnable {private final ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {lock.lock();try {System.out.println(Thread.currentThread().getName() + " - 1");System.out.println(Thread.currentThread().getName() + " - 2");System.out.println(Thread.currentThread().getName() + " - 3");} finally {lock.unlock();}}} }上面的代碼將安全地執行,而不會交錯線程。 您可能意識到我們可以使用同步塊,并且效果是相同的。 現在出現的問題是可重入鎖提供給我們什么好處?
下面介紹了使用這種類型的鎖的主要優點:
- 通過實現Lock接口提供了獲取鎖的其他方式:
- lockInterruptible :如果另一個線程擁有鎖,則當前線程將嘗試獲取解除鎖定并被阻塞,例如使用lock()方法。
- ReentrantLock類提供的其他方法,主要用于監視或測試。 例如, getHoldCount或isHeldByCurrentThread方法。
讓我們看一個使用tryLock的示例,然后再繼續下一個鎖類。
3.1嘗試獲取鎖
在下面的示例中,我們有兩個線程,試圖獲取相同的兩個鎖。
一個線程獲取lock2 ,然后阻止嘗試獲取lock1 :
public void lockBlocking() {LOGGER.info("{}|Trying to acquire lock2...", Thread.currentThread().getName());lock2.lock();try {LOGGER.info("{}|Lock2 acquired. Trying to acquire lock1...", Thread.currentThread().getName());lock1.lock();LOGGER.info("{}|Both locks acquired", Thread.currentThread().getName());} finally {lock1.unlock();lock2.unlock();} }另一個線程獲取lock1 ,然后嘗試獲取lock2 。
public void lockWithTry() {LOGGER.info("{}|Trying to acquire lock1...", Thread.currentThread().getName());lock1.lock();try {LOGGER.info("{}|Lock1 acquired. Trying to acquire lock2...", Thread.currentThread().getName());boolean acquired = lock2.tryLock(4, TimeUnit.SECONDS);if (acquired) {try {LOGGER.info("{}|Both locks acquired", Thread.currentThread().getName());} finally {lock2.unlock();}}else {LOGGER.info("{}|Failed acquiring lock2. Releasing lock1", Thread.currentThread().getName());}} catch (InterruptedException e) {//handle interrupted exception} finally {lock1.unlock();} }使用標準的鎖方法,這將導致死鎖,因為每個線程將永遠等待對方釋放該鎖。 但是,這次我們嘗試使用tryLock指定超時來獲取它。 如果四秒鐘后仍未成功,它將取消操作并釋放第一個鎖。 這將允許另一個線程解除阻塞并獲得兩個鎖。
讓我們看完整的例子:
public class TryLock {private static final Logger LOGGER = LoggerFactory.getLogger(TryLock.class);private final ReentrantLock lock1 = new ReentrantLock();private final ReentrantLock lock2 = new ReentrantLock();public static void main(String[] args) {TryLock app = new TryLock();Thread t1 = new Thread(new Worker1(app), "Thread-1");Thread t2 = new Thread(new Worker2(app), "Thread-2");t1.start();t2.start();}public void lockWithTry() {LOGGER.info("{}|Trying to acquire lock1...", Thread.currentThread().getName());lock1.lock();try {LOGGER.info("{}|Lock1 acquired. Trying to acquire lock2...", Thread.currentThread().getName());boolean acquired = lock2.tryLock(4, TimeUnit.SECONDS);if (acquired) {try {LOGGER.info("{}|Both locks acquired", Thread.currentThread().getName());} finally {lock2.unlock();}}else {LOGGER.info("{}|Failed acquiring lock2. Releasing lock1", Thread.currentThread().getName());}} catch (InterruptedException e) {//handle interrupted exception} finally {lock1.unlock();}}public void lockBlocking() {LOGGER.info("{}|Trying to acquire lock2...", Thread.currentThread().getName());lock2.lock();try {LOGGER.info("{}|Lock2 acquired. Trying to acquire lock1...", Thread.currentThread().getName());lock1.lock();LOGGER.info("{}|Both locks acquired", Thread.currentThread().getName());} finally {lock1.unlock();lock2.unlock();}}private static class Worker1 implements Runnable {private final TryLock app;public Worker1(TryLock app) {this.app = app;}@Overridepublic void run() {app.lockWithTry();}}private static class Worker2 implements Runnable {private final TryLock app;public Worker2(TryLock app) {this.app = app;}@Overridepublic void run() {app.lockBlocking();}} }如果執行代碼,將產生以下輸出:
13:06:38,654|Thread-2|Trying to acquire lock2... 13:06:38,654|Thread-1|Trying to acquire lock1... 13:06:38,655|Thread-2|Lock2 acquired. Trying to acquire lock1... 13:06:38,655|Thread-1|Lock1 acquired. Trying to acquire lock2... 13:06:42,658|Thread-1|Failed acquiring lock2. Releasing lock1 13:06:42,658|Thread-2|Both locks acquired在第四行之后,每個線程都已獲取一個鎖,并且在嘗試獲取另一個鎖時被阻塞。 在下一行,您會注意到四秒鐘的間隔。 由于我們已達到超時,因此第一個線程無法獲取鎖并釋放它已經獲取的鎖,從而允許第二個線程繼續。
4. ReentrantReadWriteLock
這種類型的鎖保留一對內部鎖( ReadLock和WriteLock )。 如接口所述,此鎖允許多個線程同時從資源讀取。 當資源具有頻繁讀取但很少寫入的資源時,這特別方便。 只要沒有需要編寫的線程,資源就將被并發訪問。
以下示例顯示了三個線程同時從共享資源讀取。 當第四個線程需要寫入時,它將排他地鎖定資源,從而防止讀取線程在寫入時訪問該資源。 一旦寫入完成并釋放了鎖定,所有讀取器線程將繼續并發訪問資源:
public class ReadWriteLockExample {private static final Logger LOGGER = LoggerFactory.getLogger(ReadWriteLockExample.class);final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();private Data data = new Data("default value");public static void main(String[] args) {ReadWriteLockExample example = new ReadWriteLockExample();example.start();}private void start() {ExecutorService service = Executors.newFixedThreadPool(4);for (int i=0; i<3; i++) service.execute(new ReadWorker());service.execute(new WriteWorker());service.shutdown();}class ReadWorker implements Runnable {@Overridepublic void run() {for (int i = 0; i < 2; i++) {readWriteLock.readLock().lock();try {LOGGER.info("{}|Read lock acquired", Thread.currentThread().getName());Thread.sleep(3000);LOGGER.info("{}|Reading data: {}", Thread.currentThread().getName(), data.getValue());} catch (InterruptedException e) {//handle interrupted} finally {readWriteLock.readLock().unlock();}}}}class WriteWorker implements Runnable {@Overridepublic void run() {readWriteLock.writeLock().lock();try {LOGGER.info("{}|Write lock acquired", Thread.currentThread().getName());Thread.sleep(3000);data.setValue("changed value");LOGGER.info("{}|Writing data: changed value", Thread.currentThread().getName());} catch (InterruptedException e) {//handle interrupted} finally {readWriteLock.writeLock().unlock();}}} }控制臺輸出顯示結果:
11:55:01,632|pool-1-thread-1|Read lock acquired 11:55:01,632|pool-1-thread-2|Read lock acquired 11:55:01,632|pool-1-thread-3|Read lock acquired 11:55:04,633|pool-1-thread-3|Reading data: default value 11:55:04,633|pool-1-thread-1|Reading data: default value 11:55:04,633|pool-1-thread-2|Reading data: default value 11:55:04,634|pool-1-thread-4|Write lock acquired 11:55:07,634|pool-1-thread-4|Writing data: changed value 11:55:07,634|pool-1-thread-3|Read lock acquired 11:55:07,635|pool-1-thread-1|Read lock acquired 11:55:07,635|pool-1-thread-2|Read lock acquired 11:55:10,636|pool-1-thread-3|Reading data: changed value 11:55:10,636|pool-1-thread-1|Reading data: changed value 11:55:10,636|pool-1-thread-2|Reading data: changed value如您所見,當寫程序線程獲得寫鎖(線程4)時,其他任何線程都無法訪問該資源。
5.結論
這篇文章展示了顯式鎖的主要實現方式,并解釋了相對于隱式鎖的一些改進功能。 這篇文章是Java Concurrency Tutorial系列的一部分。 單擊此處閱讀本教程的其余部分。
- 您可以在Github上找到源代碼。
翻譯自: https://www.javacodegeeks.com/2015/02/java-concurrency-tutorial-locking-explicit-locks.html
存儲過程 鎖定并發
總結
以上是生活随笔為你收集整理的存储过程 锁定并发_Java并发教程–锁定:显式锁定的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么让魔兽的电脑开矿(魔兽世界挖矿在哪里
- 下一篇: nifty ui_Nifty JUnit