Synchronized 从使用到原理
Java中锁的概念
自旋锁: 是指当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,知道获取到锁才会退出循环。(自旋锁就是通过CAS来不断的尝试获取执行AtomicInt/long等都是自旋锁的方式)
乐观锁:假定没有冲突,在修改数据时如果发现数据和之前获取的不一致,则读最新数据,修改后重试
悲观锁:假定会发生并发冲突,同步所有对数据的相关操作,从读数据就开始上锁。
独享锁(写):给资源加上写锁,线程可以修改资源,其他线程不能再加锁;(单写)
共享锁(读):给资源加上读锁后只能读不能改,其他线程也只能加读锁,不能加写锁(多读)
可重入锁、不可重入锁:线程拿到一把锁之后,可以自由进入同一把锁所同步的其他代码。
public class ReentrantLockDemo {
private static int i = 0;
// private final static Lock lc = new ReentrantLock(); //可重入锁
private final static Lock lc = new KaneLock(); //不可重入锁
public static void recursive() throws InterruptedException {
lc.lock();//互斥的 ---针对于多线程
i++;
System.out.println("当前的值:" + i + ",调用的线程是:" + Thread.currentThread().getName());
Thread.sleep(1000L);
recursive();
lc.unlock();
}
public static void main(String[] args) {
try {
Thread.currentThread().setName("aaaa");
recursive();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class KaneLock implements Lock {
private boolean isLocked = false; //当前线程有没有被锁
@Override
public synchronized void lock() {
while (isLocked) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isLocked = true;
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public synchronized void unlock() {
isLocked = false;
this.notify();
}
@Override
public Condition newCondition() {
return null;
}
}
公平锁、非公平锁:争抢锁的顺序,如果是按先来后到,则为公平
几种重要的锁实现方式: synchronized、ReentrantLock、ReentrantReadWriteLock.
同步关键字synchronized
- 用于实例方法、静态方法时,隐式指定锁对象
- 用于代码块时,显示指定锁对象
- 锁的作用域:对象锁、类锁、分布式锁
- 引申:如果时多个进程,怎么办 ?–不同的jvm之间需要借助第三方锁来实现了
特性:可重入、独享、悲观锁 、非公平
锁优化:锁消除(开启锁消除的参数:-XX:+DoEscapeAnalysis -XX:++EliminateLocks)
锁粗化 JDK做了锁粗化的优化,但我们自己可从代码层面优化。
public class SynchronizedDemo {
public static void main(String[] args) {
for (int i = 0; i < 10000; i++) { //循环调用
new SynchronizedDemo().test();
}
}
public void test() {
StringBuffer stringBuffer = new StringBuffer();
//synchronized (this) {//JIT触发优化,被连续调用时产生锁消除 --因为本方法只被一个线程重复调用 不存在产生安全问题
stringBuffer.append("a");
stringBuffer.append("b");
stringBuffer.append("c");
stringBuffer.append("a");
stringBuffer.append("b");
stringBuffer.append("c");
stringBuffer.append("a");
stringBuffer.append("b");
stringBuffer.append("c");
// }
}
public void test1() {
int i = 0;
synchronized (this) {
i++;
}
synchronized (this) {
i--;
}
synchronized (this) {
System.out.println("快速打印");
}
synchronized (this) {
i++;
}
//jit会锁粗化优化
synchronized (this) {
i++;
System.out.println("快速打印");
i--;
i++;
}
}
}
Note: synchronized 关键字,不仅实现同步,
JMM中规定,synchronized要保证可见性(不能够被缓存)。
那么synchronized和volatile关键字的区别是什么呢:
synchronized保证了原子性和可见性 ,只能单个线程访问
volatile只保证可见性 不保证原子性,并且可让jit不做重排序,但可能多个线程同时访问
下面的这边文章讲的比较透彻请参考:
https://blog.csdn.net/li_w_ch/article/details/112483470
思考
## 堆内存中的java对象
java对象头-Mark word
轻量级锁
重量级锁
偏向锁
锁的升级过程
对象头详解
https://zhuanlan.zhihu.com/p/138582291
下面这篇文章貌似讲的更为详细 可以参考
https://blog.csdn.net/tongdanping/article/details/79647337
这篇文章讲解了如何在cpu高的情况下找到对应的线程然后根据Jstack来查看问题
https://www.cnblogs.com/wuchanming/p/7766994.html
https://www.jianshu.com/p/9e02bed1387c