import java.util.concurrent.atomic.AtomicInteger;
/**
-
Created with IntelliJ IDEA.
-
Description: If you don’t work hard, you will a loser.
-
User: Listen-Y.
-
Date: 2020-08-04
-
Time: 20:40
*/
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
AtomicInteger atomicInteger = new AtomicInteger();
Thread thread = new Thread() {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
atomicInteger.addAndGet(1);
}
}
};
Thread thread1 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
atomicInteger.addAndGet(1);
}
}
};
thread.start();
thread1.start();
thread.join();
thread1.join();
System.out.println(atomicInteger.get());
}
}
/**
-
Created with IntelliJ IDEA.
-
Description: If you don’t work hard, you will a loser.
-
User: Listen-Y.
-
Date: 2020-08-04
-
Time: 20:53
*/
public class Demo3 {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread() {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
count++;
}
}
};
Thread thread1 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
count++;
}
}
};
thread.start();
thread1.start();
thread.join();
thread1.join();
System.out.println(count);
}
}
CAS的缺陷 ABA问题
-
这个问题就是加入现在有个num为0 有一个线程把他修改为1, 然后紧接着又有一个线程把他修改为0了 那此时仅仅通过CAS的比较是无法区分的
-
解决这个问题就需要引入额外的信息 (给变量加一个版本号 每次进行修改 都递增版本号)
synchronize的原理
- synchronize是java中的关键字,可以用来修饰实例方法、静态方法、还有代码块;主要有三种作用:可以确保原子性、可见性、有序性,原子性就是能够保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等该线程处理完数据后才能进行;可见性就是当一个线程在修改共享数据时,其他线程能够看到,保证可见性,volatile关键字也有这个功能;有序性就是,被synchronize锁住后的线程相当于单线程,在单线程环境jvm的重排序是不会改变程序运行结果的,可以防止重排序对多线程的影响。
以synchronize为例学习锁优化
编辑器和JVM配合进行的
锁消除
- 锁消除本质是以编辑器和JVM代码运行的情况智能的判断当前的锁有没有必要加 如果没有必要, 就会直接把锁干掉
/**
-
Created with IntelliJ IDEA.
-
Description: If you don’t work hard, you will a loser.
-
User: Listen-Y.
-
Date: 2020-08-04
-
Time: 21:21
*/
public class Demo4 {
public static void main(String[] args) {
StringBuffer buffer = new StringBuffer();
buffer.append(“listen”);
buffer.append(“listen”);
buffer.append(“listen”);
buffer.append(“listen”);
System.out.println(buffer);
}
}
- 到库中我们可以发现StringBuffer是加锁线程安全的 但是在我们上面写的代码中完全不用考虑线程安全问题 所以在实际运行的时候就把锁消除了
偏向锁
-
第一个尝试加锁的线程 不会真正的加锁 而是进入偏向锁(一种很轻量的锁) 知道其他线程也来竞争这把锁的时候 才会取消偏向锁的状态 真正的进行加锁
-
这个很像我去球馆打球的时候借用人家球馆里的球的时候 当人多有人和我竞争的时候我就得去花钱租 人少的时候我就可以登记一下直接玩
-
总而言之上述这俩个优化机制就是能不加锁就不加锁
自旋锁
-
当有很多线程竞争锁的时候, 偏向锁状态被消除 此时没有得到锁的线程并不会直接直接挂起放弃 而是使用自旋锁 的方式来尝试去再次获取锁
-
自旋锁能保证让其他想竞争锁的线程尽快得到锁 但是也相应付出了一定的cpu资源
-
还是上面我去球馆打球的例子 如果此时就一个人来和我竞争这个篮球 我不会立马放弃 而是会稍微等会 看我是不是快回家了
-
没有上锁就是无所状态, 在使用syn上锁的时候, 没有发生竞争就是偏向锁, 如果有少数线程发生了竞争就使用cas乐观乐观的自旋锁不断的在访问获取锁状态也就是轻量级锁,当线程访问到达十次还不能获得锁就会进入重量级锁
锁膨胀
最后
还是上面我去球馆打球的例子 如果此时就一个人来和我竞争这个篮球 我不会立马放弃 而是会稍微等会 看我是不是快回家了
- 没有上锁就是无所状态, 在使用syn上锁的时候, 没有发生竞争就是偏向锁, 如果有少数线程发生了竞争就使用cas乐观乐观的自旋锁不断的在访问获取锁状态也就是轻量级锁,当线程访问到达十次还不能获得锁就会进入重量级锁
锁膨胀
最后
[外链图片转存中…(img-2BEQTMG4-1714330602517)]
[外链图片转存中…(img-gh9sLdNh-1714330602518)]