synchronized 关键字


synchronized 关键字的作用

synchronized 关键字解决的是多个线程之间访问资源的同步性,用于保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。

作用范围

  • 修饰代码块:大括号括起来的代码,作用于调用的对象
  • 修饰方法:整个方法,作用于调用的对象
  • 修饰静态方法:整个静态方法,作用于所有对象
  • 修饰类:括号括起来的部分,作用于所有对象

其中前两种使用的锁称为对象锁,后两种称为类锁。我们来分别演示一下这几种情况:

修饰代码块和修饰方法

1.同一个对象不同线程调用

public class SynchronizedExample1 {

    private static final Logger LOGGER = LoggerFactory.getLogger(SynchronizedExample1.class);

    /**
     * 修饰一个代码块
     *
     * @param j 标识
     */
    public void test1(int j) {
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                try {
                    //每次循环睡50毫秒
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                LOGGER.info("test1 {} - {}", j, i);
            }
        }
    }

    /**
     * 修饰一个方法
     *
     * @param j 标识
     */
    public synchronized void test2(int j) {
        for (int i = 0; i < 10; i++) {
            try {
                //每次循环睡50毫秒
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LOGGER.info("test2 {} - {}", j, i);
        }
    }

    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        //创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(
                () -> example1.test1(1)
        );
        executorService.execute(
                () -> example1.test1(2)
        );
    }
}

打印结果:

16:16:09.924 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 1 - 0
16:16:09.979 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 1 - 1
16:16:10.029 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 1 - 2
16:16:10.080 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 1 - 3
16:16:10.131 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 1 - 4
16:16:10.436 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 2 - 0
16:16:10.487 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 2 - 1
16:16:10.538 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 2 - 2
16:16:10.589 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 2 - 3
16:16:10.639 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 2 - 4

从结果我们发现他确实是按照我们预期锁住了代码块,test(1) 执行完之后,再执行 test(2)。

2.不同对象不同线程调用

我们把 main 方法改成如下,再执行:

    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        //创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(
                () -> example1.test1(1)
        );
        executorService.execute(
                () -> example2.test1(2)
        );
    }

打印结果:

16:23:48.821 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 1 - 0
16:23:48.821 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 2 - 0
16:23:48.877 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 2 - 1
16:23:48.877 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 1 - 1
16:23:48.928 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 1 - 2
16:23:48.928 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 2 - 2
16:23:48.979 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 2 - 3
16:23:48.979 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 1 - 3
16:23:49.030 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 2 - 4
16:23:49.030 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test1 1 - 4

从结果看不同对象之间互不影响,所以 synchronized 修饰代码块时,作用于对象。修饰方法和修饰代码块是一样的,可以把以上例子中调用的 test1() 改成如下 test2(),执行观察结果。

修饰静态方法和修饰类

/**
     * 修饰一个类
     *
     * @param j 标识
     */
    public static void test1(int j) {
        synchronized (SynchronizedExample2.class) {
            for (int i = 0; i < 5; i++) {
                try {
                    //每次循环睡50毫秒
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                LOGGER.info("test1 {} - {}", j, i);
            }
        }
    }

    /**
     * 修饰一个静态方法
     *
     * @param j 标识
     */
    public static synchronized void test2(int j) {
        for (int i = 0; i < 5; i++) {
            try {
                //每次循环睡50毫秒
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LOGGER.info("test2 {} - {}", j, i);
        }
    }

    public static void main(String[] args) {
        //创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(
                () -> test2(1)
        );
        executorService.execute(
                () -> test2(2)
        );
    }

打印结果:

16:48:47.699 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test2 1 - 0
16:48:47.754 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test2 1 - 1
16:48:47.805 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test2 1 - 2
16:48:47.856 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test2 1 - 3
16:48:47.907 [pool-1-thread-1] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test2 1 - 4
16:48:47.958 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test2 2 - 0
16:48:48.009 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test2 2 - 1
16:48:48.060 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test2 2 - 2
16:48:48.110 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test2 2 - 3
16:48:48.161 [pool-1-thread-2] INFO com.hd.concurrency.example.sync.SynchronizedExample1 - test2 2 - 4

其实在测试之前就很明显能知道结果了,因为静态方法是属于类的,不属于某个对象,也就是说无论那个对象调用,synchronize 的锁都是静态方法所在的类。也即,synchronize 修饰静态方法和修饰类时,他是一个类锁,与调用对象无关(其实调用静态方法也不是对象的形式调用,类.test1() 的形式调用)。

synchronized 关键字的性质

  • 可重入:避免死锁、提升封装性
  • 不可中断:一旦这个锁已经被别人获得了,如果我还想获得,就只能选择等待或者阻塞,知道别的线程释放这个锁。如果别人永远都不释放这个锁,那么我只能永远等待下去。

synchronized 关键字的缺陷

  1. 效率低
  • 锁的释放情况少(执行完锁定代码块或者遇到异常退出)
  • 试图获得锁时不能设定超时时间
  • 不能中断一个正在试图获得锁的线程
  1. 不够灵活
  • 加锁和释放锁的时机单一
  • 每个锁仅有单一的条件(某个对象)
  1. 无法知道是否成功获取到锁

原子性-对比

  • synchronize:不可中断,适合竞争不激烈的场景,可读性好
  • lock:可中断,多样化同步,竞争激烈时能维持常态
  • Atomic:竞争激烈时能维持常态,比lock性能好,只能同步一个值
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

✦昨夜星辰✦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值