【ABA问题】
线程改变了一个数值,但又修改回去了,看上去跟没变一样,但实际上已经有改过了的痕迹,可能引发一些bug
CAS中存在ABA问题,但大部分情况下它不会带来bug
【JUC的常见类】
JUC指的是java.util.concurrent这个包
【Callable接口】
Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 1000; i++) {sum += i;}return sum;}};FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread t = new Thread(futureTask);t.start();System.out.println(futureTask.get());
解析:
1.创建一个匿名内部类,实现Callable接口,Callable接口内带有泛型参数,表示返回值的类型
2.重写Callable的call方法,完成累加过程,通过返回值返回计算结果
3.把callable实例用FutureTask包装一下
4.创建线程,线程构造方法内传入FutureTask,此时新线程会执行FutureTask内部Callable的call方法,完成计算,计算结果就放到了FutureTask
5.调用futureTask.get(),可以阻塞等待新线程计算完毕,并获取到FutureTask中的结果
总结:
Callable可以创建线程,并为线程封装了一个返回值,方便借助多线程的方式计算结果
【ReentrantLock(重点掌握)】
可重入锁,这是一种经典风格的锁,通过lock和unlock两种方法完成加锁
ReentrantLock locker = new ReentrantLock();Thread t1 = new Thread(() ->{for(int i = 0;i < 50000;i++){locker.lock();count++;locker.unlock();}});Thread t2 = new Thread(() ->{for(int i = 0;i < 50000;i++){locker.lock();count++;locker.unlock();}});t1.start();t2.start();t1.join();t2.join();System.out.println("count=" + count);
解析:
通过创建ReentrantLock对象的实例locker,并以实例调用lock()和unlock()来对count++进行加锁和解锁
传入参数:true,可以令ReentrantLock以公平锁的形式运行,不传入参数则以非公平锁的形式体现
【ReentrantLock和synchronized的区别】
1.synchronized是关键字(底层通过JVM的c++代码实现),ReentrantLock是标准库提供的类,通过java代码实现
2.synchronized通过代码块控制加锁解锁,ReentrantLock通过调用lock和unlock方法来完成,unlock可能会遗漏(可以把unlock放到finally中)
3.ReentrantLock提供了tryLock这样的加锁风格
前面的锁都是发现锁被别人占用了,就阻塞等待,直到加上锁为止
而tryLock在加锁失败时,不会阻塞,而是直接返回,通过返回值来反馈加锁成功/失败
4.ReentrantLock提供了公平锁的实现,默认是非公平的,但在参数中可以调整成公平的
5.ReentrantLock提供了功能更强的“等待通知”,基于Condition类来实现
【信号量Semaphore(重点掌握)】
// ReentrantLock locker = new ReentrantLock(true);Semaphore semaphore = new Semaphore(1);Thread t1 = new Thread(() ->{for(int i = 0;i < 50000;i++){
// locker.lock();try {semaphore.acquire();count++;semaphore.release();} catch (InterruptedException e) {throw new RuntimeException(e);}
// locker.unlock();}});Thread t2 = new Thread(() ->{for(int i = 0;i < 50000;i++){
// locker.lock();try {semaphore.acquire();count++;semaphore.release();} catch (InterruptedException e) {throw new RuntimeException(e);}
// locker.unlock();}});
解析:
通过调用acquire()(P操作)来申请资源,调用release()(V操作)来释放资源,达成类似于锁的效果
值为1的信号量相当于锁
信号量的值一般是0或者1,所以也有「二元信号量」的称呼
【CountDownLatch】
同时等待n个线程结束
可以衡量当前任务是否整体执行结束
把一个大任务拆成多个小任务时,借助这个组件衡量何时所有的小任务都执行完毕