【Java高併發學習】讀寫鎖、倒計時器、LockSupport、線程池


1.ReadWriteLock讀寫鎖

JDK5中提供了讀寫鎖,可以有效地減少鎖競爭提高性能。比如線程A1、A2、A3進行寫操作,B1、B2、B3進行讀操作,在使用重入鎖、內部鎖(synchronized)時,理論上所有的讀之間、寫之間、讀寫之間都是串行的。當B1在進行讀操作時,B2、B3也得等待B1的鎖資源釋放,但是讀操作並不會破壞數據的完整性,這種等待便沒有了意義。

 
非阻塞 阻塞
阻塞 阻塞

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

/**
 * 讀寫鎖測試
 * @author wsz
 * @date 2017年12月5日
 */
public class ReadWriteLockDemo {
	//重入鎖
	private static Lock lock = new ReentrantLock();
	
	//讀寫鎖
	private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	private static Lock readLock = readWriteLock.readLock();
	private static Lock writeLock = readWriteLock.writeLock();
	//測試變量
	private int value;
	
	//讀操作
	public Object handleRead(Lock lock) throws InterruptedException {
		try {
			lock.lock();
			Thread.sleep(1000);//模擬耗時
			return value;
		}finally {
			lock.unlock();
		}
		
	}
	
	//寫操作
	public void handleWrite(Lock lock,int index) throws InterruptedException {
		try {
			lock.lock();
			Thread.sleep(1000);//模擬耗時
			this.value = index;
		}finally {
			lock.unlock();
		}
	}
	
	public static void main(String[] args) {
		final ReadWriteLockDemo demo = new ReadWriteLockDemo();
		Runnable read = new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println(demo.handleRead(readLock));
//					System.out.println(demo.handleRead(lock)); 重入鎖
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		Runnable write = new Runnable() {
			@Override
			public void run() {
				try {
					demo.handleWrite(writeLock, new Random().nextInt());
//					demo.handleWrite(lock, new Random().nextInt()); 重入鎖
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		
		for(int i = 0; i< 20; i++) {
			new Thread(read).start();//讀線程並行
		}
		for(int i = 0; i< 5; i++) {
			new Thread(write).start();//寫線程串行
		}
	}
}

2.倒計時器:CountDownLatch

可以讓某一個線程等待直到倒計時結束,再開始執行。
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 倒計時器
 * @author wsz
 * @date 2017年12月5日
 */
public class CountDownLatchDemo implements Runnable{

	static final CountDownLatch cdl = new CountDownLatch(10);//參數爲計數個數
	static final CountDownLatchDemo demo = new CountDownLatchDemo();

	@Override
	public void run() {
		try {
			Thread.sleep(new Random().nextInt(10)*1000);
			System.out.println("ok");
			cdl.countDown();//完成一個線程,計數-1
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		ExecutorService pool = Executors.newFixedThreadPool(10);
		for(int i = 0; i< 15;i ++) {//線程池數量<10,將一直等待;
					//線程池數量>=10 將按時打印all over,超過的次數將繼續打印ok
			pool.submit(demo);
		}
		cdl.await();//主線程在CountDownLatch上等待,10次均已完成後,主線程才能繼續執行
		System.out.println("all over");
		cdl.countDown();
	}
}

3.線程阻塞工具類:LockSupport

  1. 在線程內任意位置讓線程阻塞
  2. 相比Thread.suspend(),彌補了由於resume()在前發生,導致線程無法繼續執行的情況
  3. 相比Object.wait(),不需要先獲得某個對象的鎖,也不會拋出InterruptedException異常
  4. LockSupport.unpark(Runnable target)可以阻塞線程,此外還提供了限時的等待方法
  5. 使用類似信號量的機制,爲每一個線程準備一個許可,如果許可可用,park()函數會立即返回,並消費這個許可,如果不可用,就會阻塞。unpark()則使一個許可變爲可用(許可不可累加)。
  6. 處於park()掛起狀態的線程爲WAITING
import java.util.concurrent.locks.LockSupport;

/**
 * LockSupport案例
 * 依然無法保證unpark()方法發生在park()方法之後。
 * LockSupport使用類似信號量的機制。它爲每一個線程提供一個許可,
 * 如果許可可用,park()函數會立即返回,並消費這個許可(將許可變爲不可用)
 * 如果許可不可用,就會阻塞,unpark()使得一個許可變爲可用(但不可累加)
 * @author wsz
 * @date 2017年12月5日
 */
public class LockSupportDemo {

	public static Object u = new Object();
	static ChangeObjectThread t1  = new ChangeObjectThread("t1");
	static ChangeObjectThread t2  = new ChangeObjectThread("t2");
	
	public static class ChangeObjectThread extends Thread{
		
		public ChangeObjectThread(String name) {
			super.setName(name);
		}
		
		@Override
		public void run() {
			synchronized(u) {
				System.out.println("in "+getName());
				LockSupport.park();
			}
			
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		
		t1.start();
		Thread.sleep(1000);
		t2.start();
		LockSupport.unpark(t1);//進行阻塞,狀態變爲WAITING
		LockSupport.unpark(t2);//進行阻塞,狀態變爲WAITING
		t1.join();
		t2.join();
	}

}

4.線程池



import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 定時任務
 * 如果任務程序本身拋出異常,後續所有執行都將被中斷
 * @author wsz
 * @date 2017年12月5日
 */
public class ScheduledExecutorServiceDemo {

	public static void main(String[] args) {

		ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
		/**
		 * scheduleAtFixedRate保證之前的任務已完成
		 * 當操作時間>調度週期2s時,後一個任務會立即執行
		 */
		ses.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);//模擬操作執行1s
					System.out.println(new Random().nextInt());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, 3, 2, TimeUnit.SECONDS);//3初始延遲,每2秒執行一次
	}
}




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