常见的锁策略
乐观锁vs悲观锁
加锁的时候预测,锁出现的竞争的概率大还是小
大--->悲观锁 小--->乐观锁
重量级锁vs轻量级锁
加锁这个操作开销大--->重量级锁
加锁这个操作开销小--->轻量级锁
挂起等待锁vs自旋锁
挂起等待锁:遇到锁冲突,就先线程阻塞,之后再唤醒
自旋锁:遇到锁冲突,先不着急阻塞,先进行重试
可重入锁vs不可重入锁
公平锁vs不公平锁
先来后到
普通互斥锁vs读写锁
普通互斥锁:加锁,解锁
读写锁:加读锁,加写锁,解锁
synchronized是可重入锁非公平锁不是读写锁,其他皆可
synchronized底层的操作
锁升级(锁的自适应过程)
无锁 没有进入代码块
|
|
|
偏向锁 一进入synchronized代码块(然而偏向锁并没有真正加锁而是标记,如果没有线程 与其竞争,那么就一直标记到"解锁",也就解除标记)
|
|
|
自旋锁 达到一定条件进入自旋锁(轻量级)
|
|
|
重量级锁 达到一定条件进入重量级锁
锁消除
针对synchronized进行的一种编译器优化,代码逻辑不变
编译器察觉到某个线程不需要加锁但是你加锁了,会尝试把锁去掉
锁粗化
锁的粒度
加锁和解锁中间的代码逻辑复杂粒度就会大
CAS
主包来简单解释一下
CAS是解决线程安全的一种方式,全称是Compare And Swap,翻译过来就是比较并且交换,对它的具体解释也是类似的
CAS是cpu上的一条指令,原子性的
++伪代码方面++:(伪代码不能够编译运行,只是表示逻辑)
java
boolean CAS(&address,ExpectValue,SwapValue){
if(&address==ExpectValue){
&address=SwapValue;
return true;
}
return false;
}
CAS又是怎样实现的
++CPU++提供了CAS这样一条指令----->
++操作系统++就可以对CAS进行封装------>
操作系统提供API,通过API就可以使用CAS机制------->
++JVM++就可以进行封装调用操作系统的API-------->
++Java应用程序++代码中就可以使用JVM的API
CAS在Java的应用
实现原子类
实际应用
java不支持运算符重载,运算符重载指的是可以让各种运算符给某个类的对象来使用
java
public class demo32 {
//不使用int,使用原子类
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Thread t1= new Thread(()->{
for (int i = 0; i <50000; i++) {
//count++;
count.getAndIncrement();
}
});
Thread t2= new Thread(()->{
for (int i = 0; i <50000; i++) {
//count++;
count.getAndIncrement();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count="+count.get());
}
}
实现自旋锁
synchronized内部"自旋"就是靠CAS,基于CAS实现的是轻量级锁,使用CAS就不必加锁,此处的锁指的是重量级锁
CAS的ABA问题
CAS循环检测判定是否有其他线程进行穿插执行了,判定依据是检测的值是否改变,但这样是不够严谨的,可能另一个线程也进行修改了但将A--->B,但随后又将B--->A,这就导致判定时认为没有
解决方案:引入"版本号"这个概念,约定版本号只能加不能减
JUC的常见类
Callable接口
在描述一个任务时,需要有返回值<T> call,而对于Runnable来说void run就不需要返回值
实际应用
带有返回值的任务可以明确此处的作用,能够更好阅读代码
java
public class demo34 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum= 0;
for (int i=1;i<=1000;i++){
sum += i;
}
return sum;
}
};
//对于Thread来说,不能以callable作为参数
//需要搭配FutureTask
FutureTask<Integer> futureTask =new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
//get方法拿到call的返回结果如果没有执行完就会进行阻塞等待
System.out.println(futureTask.get());
}
}
ReentrantLock
也是可重入锁但也和synchronized有所不同
实际应用
java
public class demo35 {
private static int count =0;
public static void main(String[] args) throws InterruptedException {
ReentrantLock Locker = new ReentrantLock();
Thread t1= new Thread(()->{
for (int i = 0; i < 50000; i++) {
//无论lock和unlock之间有return或者throws就不能够释放锁搭配try和finally九都会解锁
try {
Locker.lock();
count++;
}finally {
Locker.unlock();
}
}
});
Thread t2= new Thread(()->{
for (int i = 0; i < 50000; i++) {
try {
Locker.lock();
count++;
}finally {
Locker.unlock();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
}
}
特点
1.支持trylock
java
if(Locker.tryLock()) {
count++;
}
else {
//放弃加锁
}
lock的行为,加锁失败就会阻塞等待
trylock,加锁失败,可以直接放弃,也支持指定超时时间
2.支持公平锁
3.等待通知机制
synchronized,搭配wait¬ify,随机唤醒一个
ReentrantLock搭配Condition类,唤醒功能丰富&指定唤醒
信号量Semaphore
信号量其实就是一个"计数器",用来统计"可用资源"的个数
P:申请资源 V:释放资源
实际应用
java
public class demo36 {
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(3);
semaphore.acquire();
System.out.println("进行P操作");
semaphore.acquire();
System.out.println("进行P操作");
semaphore.acquire();
System.out.println("进行P操作");
semaphore.release();
System.out.println("进行V操作");
semaphore.acquire();
System.out.println("进行P操作");
}
}
信号量中如果计数器已经是0了,继续执行P申请资源,就会触发阻塞
Semaphore可以作为锁使用
java
public class demo37 {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore =new Semaphore(1);
Thread t1 = new Thread(()->{
for (int i = 0; i < 50000; i++) {
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
count++;
semaphore.release();
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 50000; i++) {
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
count++;
semaphore.release();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count="+count);
}
}
CountDownLatch
针对特定的场景,大任务拆解成小任务,必须等小的任务全部完成,才能算完成