JAVA锁能否保证有序性,Synchronized原理和锁升级

Synchronized使用场景Synchronized修饰实例方法:为当前实例this加锁

Synchronized修饰静态方法:为当前Class实例加锁

Synchronized修饰代码块:为Synchronized后面括号里修饰的实例加锁

注意:同一个类的不同实例拥有不同的锁,因此不会相互阻塞。

使用Synchronized修饰Class和实例时,由于Class和实例分别拥有不同的锁,因此不会相互阻塞。

如果一个线程正在访问实例的一个Synchronized修饰的实例方法时,其它线程不仅不能访问该Synchronized修饰的实例方法,该实例的其它ynchronized修饰的实例方法也不能访问,因为一个实例只有一个监视器锁,但是其它线程可以访问该实例的无Synchronized修饰的实例方法或Synchronized修饰的静态方法。

Synchronized如何保证线程安全

1.Synchronized保证原子性

Synchronized保证只有一个线程能拿到锁,进入同步代码块

2.synchronized保证可见性

执行synchronized时,对应的lock原子操作会让工作内存中从主内存中更新共享变量的值

3.synchronized保证有序性

synchronized后,虽然进行了重排序,保证只有一个线程会进入同步代码块,也能保证有序性。

Synchronized特性

可重入public class SynchronizedDemo {

private static Object obj = new Object();

public static void main(String[] args) {

Runnable sellTicket = new Runnable() {

@Override

public void run() {

synchronized (SynchronizedDemo.class) {

System.out.println("我是run");

test01(); }

}

public void test01() {

synchronized (SynchronizedDemo.class) {

System.out.println("我是test01"); }

} };

new Thread(sellTicket).start();

}

}

我是run

我是test01

synchronized是可重入锁,内部锁对象中会有一个计数器记录线程获取几次锁,在执行完同步代码块时,计数器的数量会-1,知道计数器的数量为0,就释放这个锁。

不可中断public class SynchronizedDemo {

private static Object obj = new Object();

public static void main(String[] args) throws InterruptedException {

Runnable run = () -> {

synchronized (obj) {

String name = Thread.currentThread().getName();

System.out.println(name + "进入同步代码块");

// 保证不退出同步代码块

try {

Thread.sleep(888888);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

};

// 3.先开启一个线程来执行同步代码块

Thread t1 = new Thread(run);

t1.start();

Thread.sleep(1000);

// 4.后开启一个线程来执行同步代码块(阻塞状态)

Thread t2 = new Thread(run);

t2.start();

// 5.停止第二个线程

System.out.println("停止线程前");

t2.interrupt();

System.out.println("停止线程后");

System.out.println(t1.getState());

System.out.println(t2.getState());

}

}

Thread-0进入同步代码块

停止线程前

停止线程后

TIMED_WAITING

BLOCKED

可以看到t2.interrupt()之后,t2线程仍然是阻塞状态,不可被中断

不可中断是指,当一个线程获得锁后,另一个线程一直处于阻塞或等待状态,前一个线程不释放锁,后一个线程会一直阻塞或等待,不可被中断。

synchronized属于不可被中断

Lock的lock方法是不可中断的

Lock的tryLock方法是可中断的

Synchronized原理public class SynchronizedDemo {

private int i;

public void sync() {

synchronized (this) {

i++;

}

}

}

javap命令对class文件进行反汇编,查看字节码指令如下:

88605b536885a78e0a085e1455f48582.png

可以发现synchronized同步代码块是通过加monitorenter和monitorexit指令实现的。

每个对象都有个监视器锁(monitor),当monitor被占用的时候就代表对象处于锁定状态,而monitorenter指令的作用就是获取monitor的所有权,monitorexit的作用是释放monitor的所有权public class SynchronizedDemo {

public synchronized void sync() {

}

}

javap命令对class文件进行反汇编,查看字节码指令如下:

443f8d465fd56686a1cbfd8be8870998.png

当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。

两种同步方式本质上没有区别,只是方法的同步是一种隐式的方式来实现。

Synchronized锁升级

synchronized的锁升级,说白了,就是当JVM检测到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级。

synchronized是悲观锁,在操作同步资源之前需要给同步资源先加锁,这把锁就是存在Java对象头里的。得到锁的线程能访问同步资源。

Java对象头中的MarkWord

Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。这些信息都是与对象自身定义无关的数据,所以Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。

bVcO4cm

无锁

CAS

偏向锁适用情况

一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。

偏向锁的偏是指会偏向第一个获得锁的线程。原理

当一个线程访问同步代码块并获取锁时,会通过CAS操作在Mark Word里存储锁偏向的线程ID。优点

在线程进入和退出同步块时不再通过CAS操作来加锁和解锁,而是检测Mark Word里是否存储着指向当前线程的偏向锁。

引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的锁执行操作。

轻量级锁适用情况

当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,适用在多线程交替执行同步块的情况原理

当前线程的栈帧中的创建LockRecod,将锁对象的MarkWord复制到LockRecod中,CAS操作尝试将对象的MarkWord更新为指向LockRecord的指针,如果这个更新动作成功了,那么这个线程就拥有了该对象的锁。优点

在多线程交替执行同步块的情况下,用CAS进行加锁和解锁而不是直接用重量级锁,避免性能消耗

重量级锁

monitor锁

锁升级自己的一些理解:

偏向锁是一段同步代码一直被一个线程所访问,只需要第一次用CAS存储在Mark Word里存储锁偏向的线程ID,后续就直接判断这个线程ID在不在,不需要再使用CAS了。

轻量级锁就是多线程交替执行同步块的情况下,每次都是用CAS操作尝试将对象的MarkWord更新为指向LockRecord的指针,而不是使用重量级锁阻塞其他线程。

Sychronized与Lock的区别

bVcOHaF

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值