锁
引入锁概念
代码:
package com.audition.lock;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created with IntelliJ IDEA.
*
* @Auther: 两杯水
* @Date: 2021/03/03/11:15
* @Description:
*/
public class A {
int num = 0;
public long getNum(){
return num;
}
/**
*,0.无锁控制,多线程情况下回出现问题
*/
public void incu0(){
num ++;
}
/**
* 1.有锁控制:锁住的是incu1这个内部方法
*/
public synchronized void incu1(){
num ++;
}
/**
* 2.有锁控制:锁住的是incu2这个内部方法,this是代表了incu2,此方法和1中直接在方法上加synchronized意义一样
*/
public void incu2(){
synchronized (this){
num ++;
}
}
/**
* 3.有锁控制:锁住的是整个A类,
*/
public synchronized static void incu3(){
}
/**
* 4.有锁控制:锁住的是整个A类,和3等同
*/
public void incu4(){
synchronized (A.class){
num ++;
}
}
/**
* 5.使用的是Atomic下的类,效率高,底层用了CAS操作,比较并交换
*/
AtomicInteger atomicInteger = new AtomicInteger();
public long getNum1(){
return atomicInteger.get();
}
public void incu5(){
atomicInteger.incrementAndGet();
}
}
synchronized
synchronized在锁this对象后,线程只会有一个能拿到this对象访问权限,执行其中的代码,其余的代码则等待。
等待时,其他线程干什么去了?
JDK1.6以前,在一个线程拿到锁执行代码后(Monitor),其他线程会放到一个队列中,等待线程1释放锁,这样实现性能就会有问题。
性能有问题的原因:
1.放在队列中的线程需要被唤醒
2.线程有可能会被阻塞
3.上下文切换
4.操作系统线程的调度
JDK1.6以后,引入了集成了集中状态,无状态、偏向锁、轻量级锁、重量级锁
优化原因:在jvm执行时,通常一个代码块可能在一个线程访问了很多次之后,才会有另一个线程过来访问,这样从一开始就对线程加锁就会很耗费资源,造成性能损耗,所以要优化加锁机制
优化:
偏向锁:在线程内部写一个ID(写在mark word里面的偏向锁标记位,里面有是否启用偏向锁标识0和1),下次释放完锁,之后线程又来加锁,不要做其他操作,比较下线程内部ID,如果一样,直接让线程执行。
轻量级锁:如果有多个线程来加锁,cas操作,让多个线程来争抢。
重量级锁:如果线程数量很多,很多线程都在cas外空转(线程比较少,但是空转时间长也会升级为重量级锁),会导致cpu损耗,这个时候就要用到重量级锁。放到队列中去
由于轻量级锁在进行cas操作时,另一个线程在外空转时间过长,就会引入重量级锁,所以引入另一种JDK未实现的优化,如下:
使用longAdder底层-分段CAS锁机制
分段CAS锁机制:多个线程访问同一个资源时,当一个线程在堆资源进行操作,其他线程来执行代码,底层把把资源复制为一个相同数据数组,让其他线程来访问服务出的数据资源。当线程数不多时,减少已复制的数据数组。
轻量级锁一定比重量级锁性能高?
不一定,轻量级锁如果争抢锁的时间过多,也可能会造成过多的性能损耗。
CAS:
无锁。自旋锁。乐观锁。轻量级锁
1.容易造成ABA问题:**解释:两个线程同时访问同一个内存数据,A线程短时间内修改了内存中的数据两次,一次修改为其他值,一次修改回原 值。B再去修改,没有发现数据修改了,直接改变为其他值了。**
**解决:加版本号控制**
2.原子性问题:**AtomicInteger解决方式:JAVA源代码,C++底层代码:lock cmpxchgq 来判断机器是否是多核,如果是,就在比较之前上 把缓存或者总线锁。**