八锁现象
问题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 对象