介绍
读书时很少做一些要求较高的多线程项目,导致自身对于锁的认识处于非常浅显的程度。在进入职场后也只是简单的给方法加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关键字,用法不同实现的效果也不同。以前只会修饰方法,只适用于部分场景。如今可以稍微灵活一点使用该关键字