Java synchronized 以及线程安全

一、Synchronized

Synchronized 是什么

synchronized 是 Java 中的一个关键字,用于实现线程同步,它可以应用于代码块或方法,以确保多个线程对共享资源的安全访问。
在 JDK1.6 之前,它有另一个名称叫做:重量级锁。但是从 1.6 版本起,它就在不断被优化。在此之后,锁便拥有了 4 种状态,根据锁的级别从低到高可分为:无锁、偏向锁、 轻量级锁、重量级锁。

无锁

没有对共享资源进行任何锁定,所有线程都可以去访问并修改同一资源,但同时只能有一个线程修改成功,其他线程不断尝试直至成功,并会将原内容覆盖。

偏向锁

偏向锁,指的就是偏向第一个加锁线程,对象的代码一直被同一线程执行,不存在多个线程竞争,该线程在后续的执行中自动获取锁,降低获取锁带来的性能开销。

轻量级锁

轻量级锁是指当锁是偏向锁的时候,被第二个线程 B 所访问,此时偏向锁就会升级为轻量级锁,线程 B 会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。

重量级锁

指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。

synchronized 锁升级的过程

  1. 首先,在锁对象的对象头里面有一个 threadid 字段,未访问时 threadid 为空,这时是无锁状态,任何线程都可竞争获取共享资源;

  2. 先得到共享资源的线程,其线程 ID 会被记录到 Mark Word 中,此时锁状态为偏向锁;

  3. 当后续还有线程去获取共享资源时,会先判断 threadid 是否与其线程 id 一致。如果一致则可以直接使用此对象;如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁;

  4. 自旋的线程在自旋过程中,成功获得资源 (即之前获的资源的线程执行完成并释放了共享资源),则整个状态依然处于 轻量级锁的状态,如果自旋失败;

  5. 进入重量级锁的状态,这个时候,自旋的线程进行阻塞,等待之前线程执行完成并唤醒自己。

优化过程参考文章

二、Synchronized 用法

同步代码块

使用 synchronized 关键字修饰的代码块,通过指定一个对象来确定同步范围。例如:

synchronized (obj) {
   
   // 需要同步的代码
}

在这个例子中,obj 是一个对象引用,同步块中的代码只有当线程获取了 obj 对象的锁时才能执行,确保了对共享资源的安全访问。

同步方法

使用 synchronized 关键字修饰的方法,整个方法体被视为同步代码块,锁定的对象是方法所属的对象。例如:

public synchronized void someMethod() {
   
 // 需要同步的代码
}

静态同步方法

对于静态方法,可以使用 synchronized 关键字来实现同步,此时锁定的对象是类对象。例如:

public static synchronized void someStaticMethod() {
   
   // 需要同步的代码
}

synchronized 的规则和细节:

  1. 当一个线程访问一个对象的 synchronized 代码块时,它获得的是括号中指定对象的锁。
  2. 当一个线程访问一个对象的 synchronized 实例方法时,它获得的是该对象的锁。
  3. 当一个线程访问一个类的 synchronized 静态方法时,它获得的是该类的锁。

虽然 synchronized 关键字可以简化多线程编程中的同步问题,但是因为锁的问题过度使用 synchronized 也可能导致性能问题,因为它可能会引入锁竞争和线程阻塞。

三、类锁

类锁是针对类的锁,它是针对类的所有实例的锁。当一个线程访问一个类的静态 synchronized 方法或代码块时,它获得了该类的锁,其他线程必须等待直到锁被释放。这意味着无论该类的实例有多少个,它们都共享同一个类锁。
换句话说,当一个线程获取了类锁时,其他线程无法同时访问这个类中的任何 synchronized 方法,无论是静态方法还是非静态方法。这是因为类锁是针对整个类的,而不是针对类的实例的。

四、对象锁

对象锁是针对类的实例(对象)的锁。每个 Java 对象都可以作为一个同步锁,通过 synchronized 关键字修饰的方法或代码块来获取对象锁。
当一个线程访问一个对象的 synchronized 方法或代码块时,它获得了该对象的锁,其他线程必须等待直到锁被释放。不同对象的对象锁是相互独立的,因此它们不会相互影响。

注意:不同对象的对象锁是相互独立的,因此它们不会相互影响。
既然不同对象的对象锁是相互独立的,不会相互影响。那么当不同的对象同时对共享资源进行修改时,如果没有适当的同步机制,就会存在线程安全问题。

下面看个简单的例子:

无锁的方法

public static class SharedResource {
   
   private static int COUNT;

    public void increment() {
   
        COUNT++;
        System.out.println(COUNT);
    }

}

public static void  sync1 (){
   
    SharedResource obj = new SharedResource();
    for (int i = 0; i < 100; i++) {
   
        new 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值