Java 多线程 Synchronized修饰对象

本文通过多个实验场景,探讨了Java中Synchronized关键字的工作原理。包括同一对象锁的竞争、不同对象锁的同时访问等,并分析了锁的获取与释放过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

读书时很少做一些要求较高的多线程项目,导致自身对于锁的认识处于非常浅显的程度。在进入职场后也只是简单的给方法加Synchronized关键字,但是随着项目的完善度越来越高,简单的锁住一个方法已经无法满足复杂的业务需求。这次也并非学习特别厉害的技术,只是深度了解一下Synchronized关键字是如何工作的,尤其是锁住一个对象时

准备

Demo中主要是使用List of String模拟想要锁住的对象,Main方法中用Executors模拟多线程

测试

两个线程尝试进入同一个锁

代码

public static void main(String[] args) {
    Demo demo = new Demo();
    demo.init();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(demo::block1);
    executorService.execute(demo::block1);
}

public void block1() {
    log.info("Thread {} Enter block 1", Thread.currentThread().getId());
    String robot = robotList.stream().filter(robotName -> robotName.equalsIgnoreCase("robot-01")).findAny().get();
    synchronized (robot) {
        log.info("Thread {} Enter synchronized 1", Thread.currentThread().getId());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("Thread {} Leave synchronized 1", Thread.currentThread().getId());
    }
    log.info("Thread {} Leave block 1", Thread.currentThread().getId());
}

结果

16:29:52.055 Thread 24 Enter block 1
16:29:52.055 Thread 25 Enter block 1
16:29:52.058 Thread 25 Enter synchronized 1
16:30:02.059 Thread 25 Leave synchronized 1
16:30:02.060 Thread 25 Leave block 1
16:30:02.060 Thread 24 Enter synchronized 1
16:30:12.072 Thread 24 Leave synchronized 1
16:30:12.072 Thread 24 Leave block 1

结论

当两个线程尝试进入同步代码块,线程25先获取到锁,线程24只能等待

两个线程尝试进入不同的方法,但是同一个同步代码块

代码

public static void main(String[] args) {
    Demo demo = new Demo();
    demo.init();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(demo::block1);
    executorService.execute(demo::block2);
}


public void block1() {
    log.info("Thread {} Enter block 1", Thread.currentThread().getId());
    String robot = robotList.stream().filter(robotName -> robotName.equalsIgnoreCase("robot-01")).findAny().get();
    synchronized (robot) {
        log.info("Thread {} Enter synchronized 1", Thread.currentThread().getId());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("Thread {} Leave synchronized 1", Thread.currentThread().getId());
    }
    log.info("Thread {} Leave block 1", Thread.currentThread().getId());
}

public void block2() {
    log.info("Thread {} Enter block 2", Thread.currentThread().getId());
    String robot = robotList.stream().filter(robotName -> robotName.equalsIgnoreCase("robot-01")).findAny().get();
    synchronized (robot) {
        log.info("Thread {} Enter synchronized 2", Thread.currentThread().getId());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("Thread {} Leave synchronized 2", Thread.currentThread().getId());
    }
    log.info("Thread {} Leave block 2", Thread.currentThread().getId());
}

结果

16:33:19.451 [pool-1-thread-2] INFO com.skye.Synchronized.OneObjectSynchronizedTwoMehtod.Demo - Thread 25 Enter block 2
16:33:19.451 [pool-1-thread-1] INFO com.skye.Synchronized.OneObjectSynchronizedTwoMehtod.Demo - Thread 24 Enter block 1
16:33:19.454 Thread 25 Enter synchronized 2
16:33:29.457 Thread 25 Leave synchronized 2
16:33:29.457 Thread 25 Leave block 2
16:33:29.457 Thread 24 Enter synchronized 1
16:33:39.461 Thread 24 Leave synchronized 1
16:33:39.461 Thread 24 Leave block 1

结论

虽然两个线程进入不同的方法,但是由于锁住的对象一样,线程24依然需要等待线程25释放锁

获得锁的线程在释放锁之前进入另一个方法中的同步代码块

代码

public void block1() {
    log.info("Thread {} Enter block 1", Thread.currentThread().getId());
    String robot = robotList.stream().filter(robotName -> robotName.equalsIgnoreCase("robot-01")).findAny().get();
    synchronized (robot) {
        log.info("Thread {} Enter synchronized 1", Thread.currentThread().getId());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        block2();
        log.info("Thread {} Leave synchronized 1", Thread.currentThread().getId());
    }
    log.info("Thread {} Leave block 1", Thread.currentThread().getId());
}

结果

16:36:46.761 Thread 24 Enter block 1
16:36:46.764 Thread 24 Enter synchronized 1
16:36:47.772 Thread 25 Enter block 2
16:36:56.772 Thread 24 Enter block 2
16:36:56.772 Thread 24 Enter synchronized 2
16:37:06.775 Thread 24 Leave synchronized 2
16:37:06.775 Thread 24 Leave block 2
16:37:06.775 Thread 24 Leave synchronized 1
16:37:06.776 Thread 24 Leave block 1
16:37:06.776 Thread 25 Enter synchronized 2
16:37:16.778 Thread 25 Leave synchronized 2
16:37:16.778 Thread 25 Leave block 2

结论

因为线程24已经获得锁,所以即使线程24在线程25之后进入Block 2,也能先执行

两个线程进入同一个方法,但是不同的对象

代码

public static void main(String[] args) {
    Demo demo = new Demo();
    demo.init();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() -> {
        String robotCode = "robot-01";
        demo.block1(robotCode);
    });
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    executorService.execute(() -> {
        String robotCode = "robot-02";
        demo.block1(robotCode);
    });
}

public void block1(String robotCode) {
    log.info("Thread {} Enter block 1", Thread.currentThread().getId());
    String robot = robotList.stream().filter(robotName -> robotName.equalsIgnoreCase(robotCode)).findAny().get();
    synchronized (robot) {
        log.info("Thread {} Enter synchronized 1", Thread.currentThread().getId());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        block2(robotCode);
        log.info("Thread {} Leave synchronized 1", Thread.currentThread().getId());
    }
    log.info("Thread {} Leave block 1", Thread.currentThread().getId());
}

public void block2(String robotCode) {
    log.info("Thread {} Enter block 2", Thread.currentThread().getId());
    String robot = robotList.stream().filter(robotName -> robotName.equalsIgnoreCase(robotCode)).findAny().get();
    synchronized (robot) {
        log.info("Thread {} Enter synchronized 2", Thread.currentThread().getId());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("Thread {} Leave synchronized 2", Thread.currentThread().getId());
    }
    log.info("Thread {} Leave block 2", Thread.currentThread().getId());
}

结果

16:42:30.608 Thread 24 Enter block 1
16:42:30.611 Thread 24 Enter synchronized 1
16:42:31.616 Thread 25 Enter block 1
16:42:31.616 Thread 25 Enter synchronized 1
16:42:40.619 Thread 24 Enter block 2
16:42:40.619 Thread 24 Enter synchronized 2
16:42:41.619 Thread 25 Enter block 2
16:42:41.619 Thread 25 Enter synchronized 2
16:42:50.631 Thread 24 Leave synchronized 2
16:42:50.631 Thread 24 Leave block 2
16:42:50.631 Thread 24 Leave synchronized 1
16:42:50.631 Thread 24 Leave block 1
16:42:51.629 Thread 25 Leave synchronized 2
16:42:51.629 Thread 25 Leave block 2
16:42:51.629 Thread 25 Leave synchronized 1
16:42:51.629 Thread 25 Leave block 1

结论

因为两个线程进入不同的对象的锁,所以无需等待

结束语

同样是Synchronized关键字,用法不同实现的效果也不同。以前只会修饰方法,只适用于部分场景。如今可以稍微灵活一点使用该关键字

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

skye111321

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

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

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

打赏作者

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

抵扣说明:

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

余额充值