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
- 在線程內任意位置讓線程阻塞
- 相比Thread.suspend(),彌補了由於resume()在前發生,導致線程無法繼續執行的情況
- 相比Object.wait(),不需要先獲得某個對象的鎖,也不會拋出InterruptedException異常
- LockSupport.unpark(Runnable target)可以阻塞線程,此外還提供了限時的等待方法
- 使用類似信號量的機制,爲每一個線程準備一個許可,如果許可可用,park()函數會立即返回,並消費這個許可,如果不可用,就會阻塞。unpark()則使一個許可變爲可用(許可不可累加)。
- 處於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秒執行一次
}
}