1. Lock
Lock的实现原理和Synchronized完全不同,它使用compare and swap理念,如果符合cas判定逻辑,那么就修改state状态,把当前线程设定为独占,无法获取锁就加入到等待队列。
2. Lock实现同步
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest2 {
private static ReentrantLock lock = new ReentrantLock();
static class ThreadC extends Thread {
@Override
public void run() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "---start");
Thread.sleep(1000L);
System.out.println(Thread.currentThread().getName() + "---end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
ThreadC thread = new ThreadC();
thread.setName("线程:" + i);
thread.start();
}
}
}
执行结果,可以看到各个线程是排队执行的
线程:0---start
线程:0---end
线程:1---start
线程:1---end
线程:2---start
线程:2---end
线程:3---start
线程:3---end
线程:4---start
线程:4---end
线程:5---start
线程:5---end
线程:6---start
线程:6---end
线程:7---start
线程:7---end
线程:8---start
线程:8---end
线程:9---start
线程:9---end
3. 实现部分通知
synchornized虽然有wait、notify、notifyAll,但是notify是随机通知,notifyAll是全部通知,没办法做到通知固定的线程
使用Lock配合Condition可以实现通知指定线程的功能
Condition的await、signal、signalAll分别和Object类的wait、notify、notifyAll对应
public class ThreadTest2 {
private static ReentrantLock lock = new ReentrantLock();
private static Condition conditionA = lock.newCondition();
private static Condition conditionB = lock.newCondition();
static class ThreadA extends Thread {
@Override
public void run() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "---start");
conditionA.await();
System.out.println(Thread.currentThread().getName() + "---end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "---start");
conditionB.await();
System.out.println(Thread.currentThread().getName() + "---end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws Exception {
new ThreadA().start();
new ThreadB().start();
Thread.sleep(3000L);
System.out.println("等待3秒后唤醒A线程");
try {
lock.lock();
conditionA.signalAll();
} finally {
lock.unlock();
}
Thread.sleep(3000L);
System.out.println("再等待3秒后唤醒B线程");
try {
lock.lock();
conditionB.signalAll();
} finally {
lock.unlock();
}
}
}
执行结果
Thread-0---start
Thread-1---start
等待3秒后唤醒A线程
Thread-0---end
再等待3秒后唤醒B线程
Thread-1---end
4. 公平锁与非公平锁
公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即fifo先进先出顺序。非公平锁是随机获取锁抢占机制。
可以理解为公平锁调用了wait的线程在还没获取到锁的时候,线程相当于在一个先进先出的队列里,最终谁真正获取到锁是有先来后到的顺序的;
非公平锁可以理解为在一个无序集合中,最终谁获得锁完全是靠争抢随机的。上面两句话是为了大家能更好地理解
ReentrentLock构造方法可以传一个bool类型的参数,true代表公平锁,false代表非公平锁,默认是false。
public class ThreadTest3 {
private static ReentrantLock lock = new ReentrantLock(false);
static class ThreadA extends Thread {
@Override
public void run() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "---end");
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws Exception {
ArrayList<Thread> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
ThreadA thread = new ThreadA();
thread.setName(i + "");
list.add(thread);
}
list.forEach(Thread::start);
}
}
运行结果:完全随机
0---end
1---end
2---end
4---end
6---end
8---end
3---end
7---end
5---end
9---end
把Lock构造方法的false改为true,使用公平锁结果如下
0---end
1---end
2---end
3---end
4---end
5---end
6---end
7---end
8---end
9---end
5. lockInterruptibly
看方法名字我们就知道这个方法和中断标记有关
经测试此方法只对那些线程(线程A)已经开始,还未获取到锁的线程,此时如果其他线程(线程B)设置了中断标记,则线程A会被唤醒处理中断异常。
只有等待获取锁时才会发生中断异常。如果线程还未处于等待获取锁的状态则不会发生中断异常
public class ThreadTest3 {
private static ReentrantLock lock = new ReentrantLock(true);
static class ThreadA extends Thread {
@Override
public void run() {
try {
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "---start");
for (int i = 0; i < 100000000; i++) {
Math.random();
}
System.out.println(Thread.currentThread().getName() + "---end");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "响应中断");
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws Exception {
ThreadA thread = new ThreadA();
ThreadA thread2 = new ThreadA();
thread.setName("A");
thread2.setName("B");
//thread2.interrupt();
//thread.interrupt();
//Thread.sleep(500L);
thread.start();
thread2.start();
Thread.sleep(500L);
thread2.interrupt();
thread.interrupt();
}
}
一、start之前调用中断,两个线程仍然正常执行完,所以线程start前设置中断是不管用的。
二、 thread.start();
thread2.start();
thread2.interrupt();
thread.interrupt();
这种情况可以响应中断
三、thread.start();
thread2.start();
Thread.sleep(100L);
thread2.interrupt();
thread.interrupt();
这种情况先开始并且获取到锁的线程可以正常执行完,处于等待的线程会响应中断
6. TryLock
尝试获取锁,如果获取成功返回true,否则返回false
public class ThreadTest3 {
private static ReentrantLock lock = new ReentrantLock();
static class ThreadA extends Thread {
@Override
public void run() {
try {
if (lock.tryLock()) {
System.out.println(Thread.currentThread().getName() + "---start");
for (int i = 0; i < 100000000; i++) {
Math.random();
}
System.out.println(Thread.currentThread().getName() + "---end");
} else {
System.out.println(Thread.currentThread().getName() + "---没有获取到锁");
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
public static void main(String[] args) throws Exception {
ThreadA thread = new ThreadA();
ThreadA thread2 = new ThreadA();
thread.setName("A");
thread2.setName("B");
thread.start();
thread2.start();
}
}
运行结果
B---start
A---没有获取到锁
B---end
将上述获取锁的代码改为下面的代码,代表着尝试获取锁,到时只尝试1秒,一秒后如果还获取不到就返回false
lock.tryLock(1L, TimeUnit.SECONDS)
循环部分执行时间大约4秒
执行结果
A---start
B---没有获取到锁
A---end
如果改为
lock.tryLock(10L, TimeUnit.SECONDS)
执行结果
A---start
A---end
B---start
B---end
7. awaitUninterruptibly
wait、await、sleep三个方法都可以是线程暂停,暂停期间也都可以响应中断,如果发生中断,则会抛出中断异常
condition.awaitUninterruptibly():可以实await不响应中断
public class ThreadTest3 {
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
static class ThreadA extends Thread {
@Override
public void run() {
try {
if (lock.tryLock(10L, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread().getName() + "---start");
condition.await();
System.out.println(Thread.currentThread().getName() + "---end");
} else {
System.out.println(Thread.currentThread().getName() + "---没有获取到锁");
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "---响应中断");
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
public static void main(String[] args) throws Exception {
ThreadA thread = new ThreadA();
thread.start();
Thread.sleep(1000L);
thread.interrupt();
}
}
执行结果:
Thread-0---start
Thread-0---响应中断
把上面的await()换成condition.awaitUninterruptibly(); 执行结果:
Thread-0—start
程序将一直等待signal();
**condition.awaitUntil()方法:**接受一个Date对象,标识等待到某一时间,否则就中断
8.Lock的其他方法
lock.getHoldCount():获取当前线程保持此锁定的个数,就是指当前线程调用了几次lock
lock.getQueueLength():返回正在等待获取此锁的线程估计数
lock.getWaitQueueLength(condition):返回等待与此锁给定的Condition相关的线程估计数
lock.hasQueuedThread(currentThread()):查询指定线程是否等待获取此锁
lock.hasQueuedThread():是否有线程正在等待此锁
hasWaiters(condition):是否有线程在等待与此锁指定有关的condition条件
lock.isFair():返回是否公平锁
lock.isHeldByCurrentThread():当前线程是否保持此锁定
lock.isLocked():查询此锁定是否由人以线程保持
9. ReentrantReadWriteLock
Lock常用的实现一共有两个,我们之前用的ReentrantLock和ReentrantReadWriteLock
ReentrantLock:同一个锁具有完全互斥排它的效果,虽然可以保证线程安全,但是在某些情况下,比如有读有写都需要加加锁,并且读方法的并发较高,此时用ReentrantLock显然效率会比较低。
而ReentrantReadWriteLock:内部有两个内部类ReadLock和WriteLock,称为读锁和写锁,也叫共享锁与排它锁
简单来总结就是读读不互斥,读写、写读、写写都是互斥的。
我们这里指演示一个先读后写的例子:
public class ReentrantReadWriteLockTest {
private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
static class ThreadA extends Thread {
@Override
public void run() {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + ":0:" + System.currentTimeMillis());
Thread.sleep(1000L);
System.out.println(Thread.currentThread().getName() + ":1:" + System.currentTimeMillis());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "---响应中断");
} finally {
lock.readLock().unlock();
}
}
}
static class ThreadB extends Thread {
@Override
public void run() {
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + ":0:" + System.currentTimeMillis());
Thread.sleep(1000L);
System.out.println(Thread.currentThread().getName() + ":1:" + System.currentTimeMillis());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "---响应中断");
} finally {
lock.writeLock().unlock();
}
}
}
public static void main(String[] args) throws Exception {
ThreadA threadA = new ThreadA();
threadA.setName("读");
threadA.start();
ThreadB threadB = new ThreadB();
threadB.setName("写");
threadB.start();
}
}
执行结果:
读:0:1566019964339
读:1:1566019965341
写:0:1566019965341
写:1:1566019966341
10. Timer
本想演示一下Timer的但是觉得用不到就不写了,因为Timer在时间系统变化或者其它一些情况下会有一些坑,所以不建议大家使用Timer做定时任务,推荐大家使用juc下的ScheduledThreadPoolExecutor、或quarts、分布式:es-job、xxl-job