关于Synchronized锁的到底是什么元素

 八锁现象

问题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 种不同加锁方式进行展开讨论。

this

表示的是锁住当前对象,和原来使用同步实例方式一样,锁住了当前的对象。

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();
        }
    }
}

Object

同步代码块带来了灵活性,它不再只是锁住当前对象了,可以锁住任何我们创建的对象,下面就来看看。

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();
 }
}

xxx.class

再来看看最后一种代码块锁 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 对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值