1. JDK 并发包下总结
2 . 可重入锁和不可重入锁
可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,ReentrantLock 和synchronized 都是可重入锁,可重入锁最大的作用是避免死锁
public class Test implements Runnable{
public synchronized void get(){
System.out.println(Thread.currentThread().getId());
set();
}
public synchronized void set(){
System.out.println(Thread.currentThread().getId());
}
@Override
public void run() {
get();
}
public static void main(String[] args) {
Test ss=new Test();
new Thread(ss).start();
new Thread(ss).start();
new Thread(ss).start();
}
}
public class Test implements Runnable {
ReentrantLock lock = new ReentrantLock();
public void get() {
lock.lock();
System.out.println(Thread.currentThread().getId());
set();
lock.unlock();
}
public void set() {
lock.lock();
System.out.println(Thread.currentThread().getId());
lock.unlock();
}
@Override
public void run() {
get();
}
public static void main(String[] args) {
Test ss = new Test();
new Thread(ss).start();
new Thread(ss).start();
new Thread(ss).start();
}
}
结果是每个线程均输出2次id
3 . 读写分离锁(ReentrantReadWriteLock )
读写分离锁, 可以大幅提升系统并行度.
- 读-读不互斥:读读之间不阻塞。
- 读-写互斥:读阻塞写,写也会阻塞读。
- 写-写互斥:写写阻塞。
案例如下:
public class ReentrantReadWriteLockDemo implements Runnable {
private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static ReadLock readLock = readWriteLock.readLock();
private static WriteLock writeLock = readWriteLock.writeLock();
public void get() {
readLock.lock();
System.out.println("Read currrent tread id :" + Thread.currentThread().getId());
// get();
set();
readLock.unlock();
}
public void set() {
System.out.println("set data...");
writeLock.lock();
System.out.println("Write currrent tread id :" + Thread.currentThread().getId());
writeLock.unlock();
}
@Override
public void run() {
get();
}
public static void main(String[] args) {
ReentrantReadWriteLockDemo taskDemo = new ReentrantReadWriteLockDemo();
new Thread(taskDemo).start();
// new Thread(taskDemo).start();
}
}
结果如下,读写发生了互斥:
4. 信号量(Semaphore)
信号量可以认为是一个共享锁,允许N个线程同时进入临界区, 但是超出许可范围的只能等待
案例如下:
public class SemaphoreDemo implements Runnable{
// 设置5个许可
final Semaphore semp = new Semaphore(5);
@Override
public void run() {
try {
semp.acquire();
// 模拟线程耗时操作
Thread.sleep(2000L);
System.out.println("Job done! " + Thread.currentThread().getId());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semp.release();
}
}
public static void main(String[] args){
ExecutorService service = Executors.newFixedThreadPool(20);
final SemaphoreDemo demo = new SemaphoreDemo();
for (int i = 0; i < 20; i++) {
service.submit(demo);
}
}
}
5 . 计步器(CountDownLatch)
CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行
案例如下:
public class CountDownLatchDemo implements Runnable {
private static final CountDownLatch end = new CountDownLatch(10);
private static final CountDownLatchDemo demo = new CountDownLatchDemo();
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(10) * 1000);
System.out.println("check complete!");
end.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
service.submit(demo);
}
// 等待检查
end.await();
// 所有线程检查完毕, 发射火箭.
System.out.println("fire");
service.shutdown();
}
}
7. LockSupport
LockSupprot是线程的阻塞原语,用来阻塞线程和唤醒线程。每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,并且可在线程中使用,则调用park()将会立即返回,否则可能阻塞。如果许可尚不可用,则可以调用 unpark 使其可用。但是注意许可不可重入,也就是说只能调用一次park()方法,否则会一直阻塞
LockSupport api介绍
- void park():阻塞当前线程,如果调用unpark方法或者当前线程被中断,从能从park()方法中返回
- void park(Object blocker):功能同方法1,入参增加一个Object对象,用来记录导致线程阻塞的阻塞对象,方便进行问题排查;
- void parkNanos(long nanos):阻塞当前线程,最长不超过nanos纳秒,增加了超时返回的特性;
- void parkNanos(Object blocker, long nanos):功能同方法3,入参增加一个Object对象,用来记录导致线程阻塞的阻塞对象,方便进行问题排查;
- void parkUntil(long deadline):阻塞当前线程,知道deadline;
- void parkUntil(Object blocker, long deadline):功能同方法5,入参增加一个Object对象,用来记录导致线程阻塞的阻塞对象,方便进行问题排查;
案例:
public class LockSupportDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "被唤醒");
});
thread.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.unpark(thread);
}
}