八锁现象
问题1 两个同步方法,先执行发短信还是打电话
- public class dome01 {
- public static void main(String[] args) {
- Phone phone = new Phone();
- new Thread(() -> { phone.sendMs(); }).start();
- TimeUnit.SECONDS.sleep(1);
- new Thread(() -> { phone.call(); }).start();
- }
- }
- class Phone {
- public synchronized void sendMs() {
- System.out.println("发短信");
- }
- public synchronized void call() {
- System.out.println("打电话");
- }
- }
- /*输出结果为
- 发短信
- 打电话
- */
问题2: 我们再来看:我们让发短信 延迟4s
- public class dome01 {
- public static void main(String[] args) throws InterruptedException {
- Phone phone = new Phone();
- new Thread(() -> {
- try {
- phone.sendMs();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }).start();
- TimeUnit.SECONDS.sleep(1);
- new Thread(() -> { phone.call(); }).start();
- }
- }
- class Phone {
- //synchronized现在锁定的是方法的调用者,,而两个方法使用的是同一把锁。
- public synchronized void sendMs() throws InterruptedException {
- TimeUnit.SECONDS.sleep(4);
- System.out.println("发短信");
- }
- public synchronized void call() {
- System.out.println("打电话");
- }
- }
- //结果:还是先发短信,然后再打电话!
原因:并不是顺序执行,而是synchronized 锁住的对象是方法的调用!对于两个方法用的是同一个锁, 谁先拿到谁先执行,另外一个等待
问题三
加一个普通方法
- public class dome01 {
- public static void main(String[] args) throws InterruptedException {
- Phone phone = new Phone();
- new Thread(() -> {
- try {
- phone.sendMs();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }).start();
- TimeUnit.SECONDS.sleep(1);
- new Thread(() -> { phone.hello(); }).start();
- }
- }
- class Phone {
- public synchronized void sendMs() throws InterruptedException {
- TimeUnit.SECONDS.sleep(4);
- System.out.println("发短信");
- }
- public synchronized void call() {
- System.out.println("打电话");
- }
- public void hello() {//这里没有synchronized,不受锁的影响。
- System.out.println("hello");
- }
- }
- //输出结果为
- hello
- 发短信
原因:hello是一个普通方法,不受synchronized锁的影响,不用等待锁的释放
问题四 如果我们使用的是两个对象,一个调用发短信,一个调用打电话,那么整个顺序是怎么样的呢?
- public class dome01 {
- public static void main(String[] args) throws InterruptedException {
- Phone phone1 = new Phone();
- Phone phone2 = new Phone();
- new Thread(() -> {
- try {
- phone1.sendMs();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }).start();
- TimeUnit.SECONDS.sleep(1);
- new Thread(() -> { phone2.call(); }).start();
- }
- }
- class Phone {//切记锁的是方法的调用者
- public synchronized void sendMs() throws InterruptedException {
- TimeUnit.SECONDS.sleep(4);
- System.out.println("发短信");
- }
- public synchronized void call() {
- System.out.println("打电话");
- }
- public void hello() {
- System.out.println("hello");
- }
- }
- //输出结果
- 打电话
- 发短信
原因:两个对象两把锁,不会出现等待的情况,发短信睡了4s,所以先执行打电话
问题五、六 如果我们把synchronized的方法加上static变成静态方法!那么顺序又是怎么样的呢? (1)我们先来使用一个对象调用两个方法! 答案是:先发短信,后打电话
(2)如果我们使用两个对象调用两个方法! 答案是:还是先发短信,后打电话
原因是什么呢? 为什么加了static就始终前面一个对象先执行呢!为什么后面会等待呢? 原因是:对于static静态方法来说,对于整个类Class来说只有一份,对于不同的对象使用的是同一份方 法,相当于这个方法是属于这个类的,如果静态static方法使用synchronized锁定,那么这个 synchronized锁会锁住整个对象!不管多少个对象,对于静态的锁都只有一把锁,谁先拿到这个锁就 先执行,其他的进程都需要等待!
问题七
如果我们使用一个静态同步方法、一个同步方法、一个对象调用顺序是什么?
- public class dome01 {
- public static void main(String[] args) throws InterruptedException {
- Phone phone1 = new Phone();
- // Phone phone2 = new Phone();
- new Thread(() -> {
- try {
- phone1.sendMs();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }).start();
- TimeUnit.SECONDS.sleep(1);
- new Thread(() -> { phone1.call(); }).start();
- }
- }
- class Phone {
- public static synchronized void sendMs() throws InterruptedException {
- TimeUnit.SECONDS.sleep(4);
- System.out.println("发短信");
- }
- public synchronized void call() {
- System.out.println("打电话");
- }
- public void hello() {
- System.out.println("hello");
- }
- }
- //输出结果
- 打电话
- 发短信
原因:因为一个锁的是Class类的模板,一个锁的是对象的调用者。锁住的东西不相同,所以不存在等 待,直接运行。
问题八
如果我们使用一个静态同步方法、一个同步方法、两个对象调用顺序是什么?
- public class dome01 {
- public static void main(String[] args) throws InterruptedException {
- Phone phone1 = new Phone();
- Phone phone2 = new Phone();
- new Thread(() -> {
- try {
- phone1.sendMs();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }).start();
- TimeUnit.SECONDS.sleep(1);
- new Thread(() -> { phone2.call(); }).start();
- }
- }
- class Phone {
- public static synchronized void sendMs() throws InterruptedException {
- TimeUnit.SECONDS.sleep(4);
- System.out.println("发短信");
- }
- public synchronized void call() {
- System.out.println("打电话");
- }
- public void hello() {
- System.out.println("hello");
- }
- }
- //输出结果
- 打电话
- 发短信
原因:两把锁锁的不是同一个东西
小结
锁对象:new this 具体的一个手机
锁class:static Class 唯一的一个模板
锁住代码块:
- synchronized(xxx) {
-
- }
xxx 可以是 this 或者 Object 或者 xxx.class,下面我们就根据这 3 种不同加锁方式进行展开讨论。
表示的是锁住当前对象,和原来使用同步实例方式一样,锁住了当前的对象。
- public class SynchronizedCodeTest {
-
- public static void main(String[] args) {
- SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
- for (int i = 0; i < 5; i ++) {
- Thread thread = new Thread(() -> {
- synchronizedCodeTest.testSynchronizedCode();
- });
- thread.start();
- }
- }
-
- int count = 0;
- public void testSynchronizedCode() {
- System.out.printf("%s-testSynchronizedCode-start-count=%s\n", Thread.currentThread().getName(), count);
- synchronized (this) {
- System.out.printf("%s-testSynchronizedCode-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
- count ++;
- System.out.printf("%s-testSynchronizedCode-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
- }
- System.out.printf("%s-testSynchronizedCode-end-count=%s\n", Thread.currentThread().getName(), count);
- }
-
- }
synchronized(this)
是锁住什么
- public class SynchronizedCodeTest {
-
- public static void main(String[] args) {
- for (int i = 0; i < 5; i ++) {
- SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
- Thread thread = new Thread(() -> {
- synchronizedCodeTest.testSynchronizedCode();
- });
- thread.start();
- }
- }
- }
同步代码块带来了灵活性,它不再只是锁住当前对象了,可以锁住任何我们创建的对象,下面就来看看。
- public class SynchronizedCodeTest {
-
- public static void main(String[] args) {
- Object lock = new Object();
- SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest(lock);
- for (int i = 0; i < 5; i ++) {
- Thread thread = new Thread(() -> {
- synchronizedCodeTest.testSynchroniedLock();
- });
- thread.start();
- }
- }
-
- int count = 0;
-
- Object lock = null;
- public SynchronizedCodeTest(Object lock) {
- this.lock = lock;
- }
-
- public void testSynchroniedLock() {
- System.out.printf("%s-testSynchroniedLock-start-count=%s\n", Thread.currentThread().getName(), count);
- synchronized (lock) {
- System.out.printf("%s-testSynchroniedLock-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
- count ++;
- System.out.printf("%s-testSynchroniedLock-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
- }
- System.out.printf("%s-testSynchroniedLock-end-count=%s\n", Thread.currentThread().getName(), count);
- }
-
- }
- public static void main(String[] args) {
- Object lock = new Object();
- for (int i = 0; i < 5; i ++) {
- SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest(lock);
- Thread thread = new Thread(() -> {
- synchronizedCodeTest.testSynchroniedLock();
- });
- thread.start();
- }
- }
再来看看最后一种代码块锁 Class 类,这和 public static synchronized testSynchronizedStatic()
的作用是一样的,区别就只是代码块的锁范围可变。我们直接看看代码例子。
- public class SynchronizedCodeTest {
-
- public static void main(String[] args) {
- for (int i = 0; i < 5; i ++) {
- SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
- Thread thread = new Thread(() -> {
- synchronizedCodeTest.testSynchronizedCodeClass();
- });
- thread.start();
- }
- }
-
- int count = 0;
- public void testSynchronizedCodeClass() {
- System.out.printf("%s-testSynchronizedCodeClass-start-count=%s\n", Thread.currentThread().getName(), count);
- synchronized (SynchronizedCodeTest.class) {
- System.out.printf("%s-testSynchronizedCodeClass-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
- count ++;
- System.out.printf("%s-testSynchronizedCodeClass-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
- }
- System.out.printf("%s-testSynchronizedCodeClass-end-count=%s\n", Thread.currentThread().getName(), count);
- }
-
- }
这篇介绍了「synchronizd 代码块」的 3 种使用方式,并详细介绍了各自的使用方式和区别。简单的列个表。
类型使用方式锁作用范围thissynchronized(this){}锁住当前的实例对象objectsynchronized(lock){}锁住其他实例对象,比较灵活xxx.classsynchronized(xxx.class){}锁住 Class 对象