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秒执行一次
}
}