Synchronized 用法总结

Synchronized 是 Java 多线程编程中常用关键字。Synchronized 是防止线程干扰和内存一致性的简单策略。

Synchronized 关键字用法

类对象:类名.class 创建的对象。对类对象上锁,可以叫做类锁
实例对象:当前类,通过构造方法创建的实例对象。对实例对象上锁,可以叫做实例锁(对象锁)

Synchronized 是一种同步锁,也是可重入锁。在不同地方使用 Synchronized 都会获取一把锁,但这把锁是谁的呢?

修饰普通方法

锁的是当前实例对象——实例锁

public synchronized void run() {
    // some code...
}

此写法等价于

public void run() {
    synchronized(this) {
    	// some code...   
    }
}

修饰静态方法

锁的是当前类对象——类锁

public synchronized static void run() {
    // some code...
}

此写法等价于

public void run() {
    synchronized(className.class) {
    	// some code...  
    }
}

修饰代码块

obj 可以为类锁(ClassName.class),也可以为实例锁(new ClassName)

public void run() {
    synchronized(obj) {
    	// some code...  
    }
}

不可使用的对象锁

同步块不应该加在String或基本包装类型上,如Byte、Short、Integer、Long、Float、Double、Boolean、Character。
String不能用作同步块的参数是因为String为不可变对象,任何String对象的改变都将产生一个新的String对象,这也将导致前面加的锁不会被释放。
Integer、Boolean、Double、Long不能作为同步块参数的原因是他们是基本包装类型,包装类型有特殊的逻辑,用一句话说就是Java的自动封箱和解箱操作会导致这些对象在经过运算后不再是原来的对象。
用复杂的话说就是:当把基本变量赋值给包装类型的变量(其实编译过后的操作就是调用包装类型的静态方法valueOf)或者调用静态valueOf方法时:

  • Boolean返回的是缓存的对象。
  • 整型(Byte,Short,Integer,Long)会检查该数字是否在1个字节可表示的有符号整数范围内(-128~127),是则返回缓存对象,否则返回新对象。
  • Character会缓存整型值为0~127的字符,同样会检查字符是否落在缓存范围中,是则返回,否则返回新对象。
  • Double和Float的valueOf方法始终返回新对象。

同步代码块异常

当持有锁的线程,发生异常,此时锁会释放。所以需要注意在业务中,如果代码发生异常,是否会出现错误数据的情况。

public class SynchronizedTest {

    public static void main(String[] args) {
        SyncThread syncThread = new SyncThread();
        Thread thread1 = new Thread(syncThread, "SyncThread1");
        Thread thread2 = new Thread(syncThread, "SyncThread2");
        thread1.start();
        thread2.start();
    }

}


class SyncThread implements Runnable {

    int i = 0;

    @SneakyThrows
    public synchronized void run() {
        while (true) {
            System.out.println("Thread name : " + Thread.currentThread().getName() + " -> i = " + i);
            Thread.sleep(1000);
            if (i++ == 3) {
                int result = 1 / 0;
            }
        }
    }

}

结果为:

Thread name : SyncThread1 -> i = 0
Thread name : SyncThread1 -> i = 1
Thread name : SyncThread1 -> i = 2
Thread name : SyncThread1 -> i = 3
Exception in thread "SyncThread1" java.lang.ArithmeticException: / by zero
	at cn.liaocp.amireux.base.util.SyncThread.run(SynchronizedTest.java:31)
	at java.base/java.lang.Thread.run(Thread.java:829)
Thread name : SyncThread2 -> i = 4
Thread name : SyncThread2 -> i = 5
Thread name : SyncThread2 -> i = 6
Thread name : SyncThread2 -> i = 7
Thread name : SyncThread2 -> i = 8
...

分析结果

按代码逻辑,如果没有 int result = 1 / 0;,线程2 将永远没有机会执行。但在线程1 发生异常后,线程2 执行了,说明线程以发生异常,释放了锁,线程2 获得了锁,线程2 执行。

用法总结

  1. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。

  2. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

  3. 需注意线程发生异常处理。

参考链接
Java中Synchronized的用法
为什么Synchronized不能加在String和Integer等基本包装类型上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
synchronized用法是为了实现多线程程序中的同步操作,确保多个线程在访问共享资源时的安全性。在Java中,可以使用synchronized关键字来标记方法或代码块,并确保同一时间只有一个线程可以执行被标记的部分。 在给出的引用中,有几个示例展示了synchronized用法: 1. 在SynchronizedUsage类中,定义了两个同步方法synchronizedMethod1和synchronizedMethod2以及一个普通方法unsafeMethod。这些方法都使用了synchronized关键字,以保证在同一时间只有一个线程可以执行这些方法。 2. 在SynchronizedUsageChild类中,继承了SynchronizedUsage类,并重写了同步方法synchronizedMethod1。子类中的同步方法中调用了父类的同步方法super.synchronizedMethod1,这验证了synchronized关键字的可重入性。意思是当一个线程已经获得了一个对象的锁,它可以再次进入该对象的其他同步方法或代码块。 3. 在SynchronizedUsage类中还定义了一个同步的静态方法synchronizedStaticmethod。这个方法使用了static关键字和synchronized关键字,以保证在同一时间只有一个线程可以执行这个静态方法。 总结来说,synchronized用法可以分为以下几点: - 可以标记方法或代码块来实现同步 - 使用synchronized关键字来确保同一时间只有一个线程可以执行被标记的部分 - 可以实现可重入性,即同一个线程可以再次进入同步方法或代码块 - 静态方法可以使用synchronized关键字来实现同步 请注意,这只是synchronized用法的一部分,还有很多其他方面和细节需要考虑。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值