1、ReentrantLock打断锁机制
ReentrantLock还可以lock.lockInterruptibly()这个方法,对interrupt()方法做出相应,可以被打断的加锁。
比如t1线程调用了lock.lockInterruptibly(),一直sleep。我们可以调用t2.interrupt。从而打断t2线程的等待。使用原来的lock.lock()是无法打断的。这就是ReentrantLock比synchronized好用的地方
package com.example.demo.gc20;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T04_ReentrantLock4 {
// RenntrantLock的锁打断机制
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Thread t1 = new Thread( () ->{
try {
lock.lock();
System.out.println("t1 start");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
System.out.println("t1 end");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
});
t1.start();
Thread t2 = new Thread(() -> {
try {
lock.lockInterruptibly();
System.out.println("t2 start");
TimeUnit.SECONDS.sleep(5);
System.out.println("t2 stop");
}catch (InterruptedException e){
System.out.println("InterruptedException");
}finally {
lock.unlock();
}
});
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
t2.interrupt(); // 打破t2线程等待
}
}
2、公平锁与非公平锁
ReentrantLock可以指定为公平锁。它默认是非公平的。
公平锁的意思是当我们new一个ReentrantLock可以传参数true。true为公平锁。false为非公平锁
公平锁:谁等在前面就先让谁执行。
package com.example.demo.gc20;
import java.util.concurrent.locks.ReentrantLock;
public class T05_ReentrantLock5 extends Thread {
// 公平锁
private static ReentrantLock lock = new ReentrantLock(true);
public void run(){
for(int i=0;i<100;i++){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"获得锁");
}finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
T05_ReentrantLock5 t5 = new T05_ReentrantLock5();
Thread t1 = new Thread(t5);
Thread t2 = new Thread(t5);
t1.start();
t2.start();
}
}
####ReentrantLock vs synchronized
ReentrantLock可以替代synchronized是没有问题的。他也可以重入。可以锁定。本身底层是cas。
trylock:自己来控制
lockInterruptibly:指定当前线程中间可以被打断
interrupt:线程停止等待
现在除了synchronized之外,多数内部都是用cas。这个lock不像sync那样直接进入阻塞状态。至少前面还有一个cas状态
3、CountDownLatch(倒数门栓)
package com.example.demo.gc20;
import java.util.concurrent.CountDownLatch;
public class T06_TestCountDownLatch {
// 倒数门栓
public static void main(String[] args) {
usingJoin();
usingCountDownLatch();
}
private static void usingCountDownLatch() {
Thread [] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i=0;i<threads.length;i++){
threads[i] = new Thread(() -> {
int result = 0;
for (int j=0;j<10000;j++){
result += j;
latch.countDown();
}
});
}
for (int i = 0;i<threads.length;i++){
threads[i].start();
}
try {
latch.await();
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("end latch");
}
private static void usingJoin(){
Thread[]threads = new Thread[100];
for (int i=0;i<threads.length;i++){
threads[i] = new Thread(() ->{
int result = 0;
for (int j=0; j<10000;j++){
result += j;
}
});
}
for (int i=0;i<threads.length;i++){
threads[i].start();
}
for(int i = 0;i<threads.length;i++){
try{
threads[i].join();
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("end join");
}
}
####join和倒数门栓的用法区别。
join用法没那么灵活。就是哪个线程在当前线程执行后调用join方法。就它执行完毕后当前线程再执行。
而倒数门栓是。new 一个倒数门栓计数。每一个线程结束就latch.countdown()也就是count–。这时候程序一直在latch.await()这里。插着就插着。并且计数。100,99,98这样。一直到count为0的时候,门栓才会被打开。它是用来等待线程结束
4、cyclicBarrier(循环栅栏)
使用场景:我们需要并发执行,不同线程去执行不同操作。有的线程去数据库操作,有的线程去网络访问。有的线程去读文件。必须是这三个线程都到位了以后。才能执行操作。这时候就可以用cyclicBarrier
package com.example.demo.gc20;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class T07_TestCyclicBarrier {
// 循环栅栏
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(20,() ->
System.out.println("满人"));
for (int i=0;i<100;i++){
new Thread(() -> {
try {
barrier.await();
}catch (InterruptedException e){
e.printStackTrace();
}catch (BrokenBarrierException e){
e.printStackTrace();
}
}).start();
}
}
}