1. 程式人生 > >Java多執行緒核心技術(四):Lock的使用

Java多執行緒核心技術(四):Lock的使用

一、使用ReentrantLock類

1、ReentrantLock的簡單使用

程式碼示例:

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

class MyService{
	private Lock lock = new ReentrantLock();
	public void methodA(){
		try{
			lock.lock();
			System.out.println("methodA begin ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println("methodA   end ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());			
		}catch(InterruptedException e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
	public void methodB(){
		try{
			lock.lock();
			System.out.println("methodB begin ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println("methodB   end ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());			
		}catch(InterruptedException e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
}
class ThreadA extends Thread{
	private MyService service;
	public ThreadA(MyService service){
		this.service = service;
	}
	@Override
	public void run(){
		service.methodA();
	}
}
class ThreadB extends Thread{
	private MyService service;
	public ThreadB(MyService service){
		this.service = service;
	}
	@Override
	public void run(){
		service.methodB();
	}
}
public class TestClass{
	public static void main(String[] args){	
		MyService service = new MyService();
		ThreadA a = new ThreadA(service);
		a.setName("A");
		ThreadA aa = new ThreadA(service);
		aa.setName("AA");
		ThreadB b = new ThreadB(service);
		b.setName("B");
		ThreadB bb = new ThreadB(service);
		bb.setName("BB");
		a.start();
		aa.start();
		b.start();
		bb.start();
	}
}

2、使用Condition實現等待/通知

關鍵字synchronized與wait()/notify()/notifyAll()方法相結合可以實現等待/通知模式,類ReetrantLock也可以實現同樣的功能,但需要藉助Condition物件。Condition類可以實現多路通知功能,也就是一個Lock物件裡面可以建立多個Condition例項,執行緒物件可以註冊在指定的Condition中,從而可以有選擇性地進行執行緒通知,在排程執行緒上更靈活。

下面是使用多個Condition實現通知部分執行緒:

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

class MyService{
	private Lock lock = new ReentrantLock();
	public Condition conditionA = lock.newCondition();
	public Condition conditionB = lock.newCondition();
	public void awaitA(){
		try{
			lock.lock();
			System.out.println("awaitA begin ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
			conditionA.await();
			System.out.println("awaitA   end ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());			
		}catch(InterruptedException e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
	public void awaitB(){
		try{
			lock.lock();
			System.out.println("awaitB begin ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
			conditionB.await();
			System.out.println("awaitB   end ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());			
		}catch(InterruptedException e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
	public void signalAll_A(){
		try{
			lock.lock();
			System.out.println("signalAll_A ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
			conditionA.signalAll();//喚醒所有在conditionA上等待的執行緒
		}finally{
			lock.unlock();
		}
	}
	public void signalAll_B(){
		try{
			lock.lock();
			System.out.println("signalAll_B ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
			conditionB.signalAll();//喚醒所有在conditionA上等待的執行緒
		}finally{
			lock.unlock();
		}
	}
}
class ThreadA extends Thread{
	private MyService service;
	public ThreadA(MyService service){
		this.service = service;
	}
	@Override
	public void run(){
		service.awaitA();
	}
}
class ThreadB extends Thread{
	private MyService service;
	public ThreadB(MyService service){
		this.service = service;
	}
	@Override
	public void run(){
		service.awaitB();
	}
}
public class TestClass{
	public static void main(String[] args) throws InterruptedException{	
		MyService service = new MyService();
		ThreadA a = new ThreadA(service);
		a.setName("A");
		ThreadA aa = new ThreadA(service);
		aa.setName("AA");
		ThreadB b = new ThreadB(service);
		b.setName("B");
		ThreadB bb = new ThreadB(service);
		bb.setName("BB");
		a.start();
		aa.start();
		b.start();
		bb.start();
		Thread.sleep(3000);
		service.signalAll_A();
	}
}
執行結果如下:



3、公平鎖與非公平鎖

鎖Lock分為“公平鎖”和“非公平鎖”,公平鎖表示執行緒獲取鎖的順序是按照執行緒加鎖的順序來分配的,即先來先得的FIFO先進先出順序。而非公平鎖就是一種獲得鎖的搶佔機制,是隨機獲得鎖的。建立公平鎖就是在ReentrantLock類的建構函式中傳入true:Lock lock = new ReentrantLock(true);

4、ReentrantLock的幾個方法

(1)getHoldCount()的作用是查詢當前執行緒保持此鎖定的個數,也就是呼叫lock()方法的次數。

(2)getQueueLength()的作用是返回正在等待獲取此鎖定的執行緒估計數。

(3)int getWaitQueueLength(Condition condition)的作用是返回等待與此鎖定相關的給定條件Condition的執行緒估計數。

(4)boolean hasQueuedThread(Thread thread)的作用是查詢指定的執行緒是否正在等待獲取此鎖定。

(5)boolean hasQueuedThreads()的作用是查詢是否有執行緒正在等待獲取此鎖定。

(6)boolean hasWaiters(Condition condition)的作用是查詢是否有執行緒正在等待與此鎖定有關的condition條件。

(7)boolean isFair()的作用是判斷是不是公平鎖。

(8)boolean isHeldByCurrentThread()的作用是查詢當前執行緒是否保持此鎖定。

(9)boolean isLocked()的作用是查詢此鎖定是否由任意執行緒保持。

(10)void lockInterruptibly()的作用是:如果當前執行緒未被中斷,則獲取鎖定,如果已經被中斷則出現異常。

(11)boolean tryLock()的作用是,僅在呼叫時鎖定未被另一個執行緒保持的情況下,才獲取該鎖定。

(12)boolean tryLock(long timeout,TimeUnit unit)的作用是,如果鎖定在給定等待時間內沒有被另一個執行緒保持,且當前執行緒未被中斷,則獲取該鎖定。

二、使用ReentrantReadWriteLock類

類ReentrantLock具有完全互斥排他的效果,即同一時間只有一個執行緒在執行ReentrantLock.lock()方法後面的任務。這樣做雖然保證了例項變數的執行緒安全性,但效率很低。使用讀寫鎖ReentrantReadWriteLock類可以解決這個問題。讀寫鎖表示有兩個鎖,一個是讀操作相關的鎖,也稱為共享鎖;另一個是寫操作相關的鎖,也叫排他鎖。也就是多個讀鎖之間不互斥,讀鎖和寫鎖互斥,寫鎖和寫鎖互斥。

如下是讀鎖和寫鎖的使用示例:

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

class MyService{
	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
	public void read(){
		try{
			lock.readLock().lock();
			System.out.println("獲得讀鎖  ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			lock.readLock().unlock();
		}
	}
	public void write(){
		try{
			lock.writeLock().lock();
			System.out.println("獲得寫鎖  ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			lock.writeLock().unlock();
		}
	}
}
class ThreadA extends Thread{
	private MyService service;
	public ThreadA(MyService service){
		this.service = service;
	}
	@Override
	public void run(){
		service.read();
	}
}
class ThreadB extends Thread{
	private MyService service;
	public ThreadB(MyService service){
		this.service = service;
	}
	@Override
	public void run(){
		service.write();
	}
}
public class TestClass{
	public static void main(String[] args) throws InterruptedException{	
		MyService service = new MyService();
		ThreadA a = new ThreadA(service);
		a.setName("A");
		ThreadB b = new ThreadB(service);
		b.setName("B");
		a.start();
		b.start();		
	}
}
執行結果如下:



相關推薦

Java執行核心技術()Lock的使用

一、使用ReentrantLock類 1、ReentrantLock的簡單使用 程式碼示例: import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;

Java執行核心技術()Lock的使用

本文主要介紹使用Java5中Lock物件也能實現同步的效果,而且在使用上更加方便。 本文著重掌握如下2個知識點: ReentrantLock 類的使用。 ReentrantReadWriteLock 類的使用。 1. 使用ReentrantLock 類 在Java多執行緒中,可以使用 synchron

Java執行核心技術(一)基礎知識總結

概念 程序:程序是作業系統結構的基礎,是一次程式的執行,是一個程式及其資料在處理機上順序執行時所發生的活動,是程式在一個程式集合上執行的過程,它是系統進行資源分配和排程的一個基本單位。 執行緒:執行緒是程序中獨立執行的子任務。使用多執行緒技術後,可以在同一時間執行更多不同種

Java執行核心技術(五)定時器Timer

Timer類主要負責計劃任務的功能,也就是在指定的時間開始執行某一個任務。 1、schedule(TimerTask task,Date time) 方法schedule(TimerTask task,Date time)的作用是在指定的日期執行一次某一任務。 如下程式碼是

Java執行核心技術(五)單例模式與執行

本文只需要考慮一件事:如何使單例模式遇到多執行緒是安全的、正確的 1.立即載入 / "餓漢模式" 什麼是立即載入?立即載入就是使用類的時候已經將物件建立完畢,常見的實現辦法就是直接 new 例項化。 public class MyObject { private static MyObject m

Java執行核心技術(六)執行組與執行異常

本文應注重掌握如下知識點: 執行緒組的使用 如何切換執行緒狀態 SimpleDataFormat 類與多執行緒的解決辦法 如何處理執行緒的異常 1.執行緒的狀態 執行緒物件在不同執行時期有不同的狀態,狀態資訊就處於State列舉類中,如圖所示: 執行緒狀態 初始(NEW):新建立了一個執行緒物件,但還

從ConcurrentHashMap的演進看Java執行核心技術

執行緒不安全的HashMap 眾所周知,HashMap是非執行緒安全的。而HashMap的執行緒不安全主要體現在resize時的死迴圈及使用迭代器時的fast-fail上。 注:本章的程式碼均基於JDK 1.7.0_67 HashMap工作原理 HashMap資料結構

JAVA執行機制第(末篇)執行常用方法總結和執行同步

執行緒的常用方法: 這裡我覺得這個老師的教案總結的很舒胡(主要是懶~):  執行緒同步: 在處理多執行緒問題時,有一個Bug問題啊:當兩個或多個執行緒同時訪問一個父類變數時,並且一個執行緒需要修改這個變數,(一個執行緒讓變數A增加,另一個執行緒讓變數A減少)。 所

Java執行(十Timer

Timer schedule(TimerTask task, Date time) 該方法在指定日期執行任務,如果是過去的時間,這個任務會立即被執行。 執行時間早於當前時間 示例程式碼,當前時間是2019年9月19日,程式碼中寫的是前一天的時間。 public class MyTask1 extends Ti

Java執行學習筆記() 使用ReentrantLock實現同步

1. 測試1 1.1 MyService import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MyService { pr

Java執行程式設計筆記2synchronized同步方法

非執行緒安全會在多個執行緒對同一個物件中的例項變數進行併發訪問時發生,產生的結果就是髒讀,也就是取到的資料是被更改過的。執行緒安全就是獲得的例項變數的值是經過同步處理的。 方法內的變數是執行緒安全的 方法內的變數是執行緒安全的。非執行緒安全的問題存在於例項變數中,如果是方法內部的私有變數,不存在非執行緒安

java執行-初探(

java多執行緒-初探(三)   本文闡述經典的多執行緒生產者、消費者模型。 涉及執行緒等待、喚醒、死鎖以及常用的synchronized跟JDK5的Lock介面兩種方式的知識點。   生產者、消費者模型初步理解 生產者:負責給資源中儲存資料(資源)

Java執行(一)執行基礎及建立

(一)、執行緒的生命週期 新建狀態: 使用 new 關鍵字和 Thread 類或其子類建立一個執行緒物件後,該執行緒物件就處於新建狀態。它保持這個狀態直到程式 start() 這個執行緒。 就緒狀態: 當執行緒物件呼叫了start()方法之後,該執行緒就進入就緒

執行(二)Java執行,啟動執行,兩個執行加一,另外兩個執行減一

  public class Test { public static void main(String[] args) { final ShareData data = new ShareData(); for (int i = 0; i < 2; i++) {

Java執行程式設計筆記10單例模式

立即載入:“餓漢模式” 立即載入就是指使用類的時候已經將物件建立完畢,常見的實現方法就是直接new例項化。也就是在呼叫方法前,例項就被建立了。示例程式碼如下所示: class MyObject { private static MyObject myObject=new MyObject();

JAVA執行實現的種方式

         昨天自己用ExecutorService建立執行緒池做穿透測試了一下,感覺挺有意思,所以又好好的看了一下執行緒的問題,在此轉載了一篇博友的文章,感覺總結的不錯,所以分享一下. Java多執行緒實現方式主要有四種: 繼承Thread類、實現Runnable

JAVA執行機制第二彈(程式碼)Thread的子類建立執行

在Java中,執行緒物件的建立是用Threa類或者它的子類建立。 在編寫Thread類的子類時,需要重寫父類的run()方法,其目的是規定執行緒的具體操作,否則執行緒就沒有開始的地方 在這裡,做一個小小總結:  ··線上程的編寫的時候,要重寫父類的run()方法,在ru

java執行(1)執行的建立和執行的安全問題

前言 java多執行緒多用於服務端的高併發程式設計,本文就java執行緒的建立和多執行緒安全問題進行討論。 正文 一,建立java執行緒 建立java執行緒有2種方式,一種是繼承自Thread類,另一種是實現Runnable介面。由於java只支援單

Java執行下載技術實現

多執行緒下載 多執行緒下載技術,簡單的說就是把要下載的檔案分成幾塊,由不同的執行緒來負責每一塊資料的下載任務。 技術要點 RandomAccessFile: Java中用來實現隨機訪問檔案的類 http Range請求頭 具體思路 1、檔案分塊。 檔案分塊大小(blockSize)= (

執行基礎之Linux提供的原子鎖型別atomic_t

在x86體系下,任何處理器平臺下都會有一些原子性操作,在單處理器情況下,單步指令的原子性容易實現。但是在SMP多處理器情況下,只有那些單字的讀(將變數讀進暫存器)或寫(從暫存器寫入到變數地址)才是原子性的。故而在SMP下,要保證特定指令集合的原子性即不被中斷,x