多线程锁机制(一)
并发场景demo
package com.lock;
public class LockDemo {
public static void main(String[] args) throws InterruptedException {
A a = new A();
long start = System.currentTimeMillis();
Thread t1 = new Thread(()->{
for(int i=0;i<10000000;i++){
a.increase();
}
});
t1.start();
for(int i=0;i<10000000;i++){
a.increase();
}
t1.join();
long end = System.currentTimeMillis();
System.out.println(String.format("%sms", end-start));
System.out.println(a.getNum());
}
}
很简单的一个demo,创建一个a对象,创建线程t1和主线程执行a对象的increase方法
package com.lock;
public class A {
int num = 0;
public void increase(){
num++;
}
public long getNum(){
return num;
}
}
对象A的increase方法进行对num变量执行加1操作
从上图中控制台可以看出线程t1和主线程同时执行1千万次加一操作,结果不等于二千万;这就是线程的不安全问题。
解决方式
一、添加synchronized关键字
package com.lock;
public class A {
int num = 0;
public synchronized void increase(){
num++;
}
public long getNum(){
return num;
}
}
在increase方法前加synchronized关键字进行修饰,对调用当前方法的对象进行加锁。
上面的方式等同于
package com.lock;
public class A {
int num = 0;
public void increase(){
synchronized (this) {
num++;
}
}
public long getNum(){
return num;
}
}
synchronized关键字底层锁机制:互斥锁、悲观锁、同步锁、重量级锁
当线程t1加锁成功后,主线程出现线程阻塞、上下文切换,而当线程t1释放锁后,操作系统需要唤醒其他线程,也就是操作系统的线程调度
由于以上原因耗时久,性能差,所以叫做重量级锁。
上图的计算结果对的,但是时间要长,可见牺牲了部分性能
二、原子操作类
package com.lock;
import java.util.concurrent.atomic.AtomicInteger;
public class A {
AtomicInteger atomicInteger = new AtomicInteger();
int num = 0;
public void increase(){
/*synchronized (this) {
num++;
}*/
atomicInteger.incrementAndGet();
}
public long getNum(){
return num;
}
}
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
//获取对象var1中valueOffset的属性值
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
/*将获取到的var5与对象var1中valueOffset的属性值进行比较。
若一致,则将var5+var4赋值给对象var1中的valueOffset,并结束循环,返回var5旧值
若不一致,则compareAndSwapInt返回一个false,继续循环
compareAndSwapInt是由本地方法C++实现,内部有比较、赋值两步,其C++实现有锁机制。*/
return var5;
}
CAS:无锁、自旋锁、乐观锁、轻量级锁
相比较synchronized关键字的方式:
- 没有线程阻塞(上面代码可以看出,如果比较的值不相等,则一直在循环。)
- 操作系统无需唤醒线程
所以相比synchronized而言,性能更好,所以也可称为轻量级锁。