一、简介
所谓线程八锁,其实就是就是线程锁的8种情况,对应于是否加上synchronized,是否加上static等8种常见情况,具体见下面详解。
二、线程8锁
- 第一种情况: 两个线程调用同一个对象的两个普通同步方法,先打印one还是two?
/**
* 线程八锁第一种情况: 两个线程调用同一个对象的两个普通同步方法,先打印one还是two?
* <p>
* 运行结果: 可能先打印one,也可能先打印two
* <p>
* 解释:
* 因为调用start()方法启动线程的时候,具体是由CPU调度获取执行权,所以严格来说顺序不一定.
* 被synchronized修饰的方法,锁的对象是方法的调用者shareResource。因为两个方法的调用者是同一个,所以两个方法用的是同一个锁,先调用方法的先执行,具体由CPU调度决定.
*/
public class Test8LockDemo {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(shareResource::one).start();
new Thread(shareResource::two).start();
}
}
/**
* 共享资源类
*/
class ShareResource {
public synchronized void one() {
System.out.println("one...");
}
public synchronized void two() {
System.out.println("two...");
}
}
运行结果:
one...
two...
解释:
因为调用start()方法启动线程的时候,具体是由CPU调度获取执行权,所以严格来说顺序不一定.
被synchronized修饰的方法,锁的对象是方法的调用者shareResource。因为两个方法的调用者是同一个,所以两个方法用的是同一个锁,先调用方法的先执行,具体由CPU调度决定.
- 第二种情况: 给one()方法加一点睡眠暂停,先打印one还是two?
/**
* 线程八锁第二种情况: 给one()方法加一点睡眠暂停,先打印one还是two?
* <p>
* 运行结果: 经过5秒后先打印one,再打印two
* <p>
* 解释:
* TimeUnit.SECONDS.sleep方法不会释放对象锁,所以锁的是同一个对象shareResource,
* 某个时刻只能有一个线程进入到同一个对象锁的同步方法内,所以two方法必须等有第一个方法执行完释放锁之后才能执行.
*/
public class Test8LockDemo {
public static void main(String[] args) throws InterruptedException {
ShareResource shareResource = new ShareResource();
new Thread(shareResource::one).start();
//这里加一点延迟,为了保证前面的线程先获取执行权.
Thread.sleep(1000);
new Thread(shareResource::two).start();
}
}
/**
* 共享资源类
*/
class ShareResource {
public synchronized void one() {
//one()方法睡眠5秒
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one...");
}
public synchronized void two() {
System.out.println("two...");
}
}
运行结果:
one...
two...
解释:
TimeUnit.SECONDS.sleep方法不会释放对象锁,所以锁的是同一个对象shareResource,
某个时刻只能有一个线程进入到同一个对象锁的同步方法内,所以two方法必须等有第一个方法执行完释放锁之后才能执行.
- 第三种情况: 新增一个普通方法three(),先打印one还是three?
/**
* 线程八锁第三种情况: 新增一个普通方法three(),先打印one还是three?
* <p>
* 运行结果: 先打印three...,过五秒后打印one...
* <p>
* 解释:
* three()是普通方法,执行的时候无需判断是否拥有锁对象,不受锁的迎新,所以不需要等待直接执行,故先打印three...
*/
public class Test8LockDemo {
public static void main(String[] args) throws InterruptedException {
ShareResource shareResource = new ShareResource();
new Thread(shareResource::one).start();
//这里加一点延迟,为了保证前面的线程先获取执行权.
Thread.sleep(1000);
new Thread(shareResource::three).start();
}
}
/**
* 共享资源类
*/
class ShareResource {
public synchronized void one() {
//one()方法睡眠5秒
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one...");
}
public synchronized void two() {
System.out.println("two...");
}
public void three() {
System.out.println("three...");
}
}
运行结果:
three...
one...
解释:
three()是普通方法,执行的时候无需判断是否拥有锁对象,不受锁的迎新,所以不需要等待直接执行,故先打印three...
- 第四种情况: 两个普通同步方法,两个ShareResource对象,先打印one还是two
/**
* 线程八锁第四种情况: 两个普通同步方法,两个ShareResource对象,先打印one还是two
* <p>
* 运行结果: 先打印two...,经过5秒后打印one...
* <p>
* 解释:
* 由于有两个对象shareResource和shareResource2,分别调用各自的同步方法,被synchronized修饰的方法,
* 锁的对象是方法的调用者,所以各自锁定自己的对象,两个方法的调用者不是同一个,所以两个方法用的不是同一个锁,
* 两者互不影响,由于one()方法有延时,所以先打印two...
*/
public class Test8LockDemo {
public static void main(String[] args) throws InterruptedException {
ShareResource shareResource = new ShareResource();
ShareResource shareResource2 = new ShareResource();
new Thread(shareResource::one).start();
//这里加一点延迟,为了保证前面的线程先获取执行权.
Thread.sleep(1000);
new Thread(shareResource2::two).start();
}
}
/**
* 共享资源类
*/
class ShareResource {
public synchronized void one() {
//one()方法睡眠5秒
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one...");
}
public synchronized void two() {
System.out.println("two...");
}
}
运行结果:
two...
one...
解释:
由于有两个对象shareResource和shareResource2,分别调用各自的同步方法,被synchronized修饰的方法,
锁的对象是方法的调用者,所以各自锁定自己的对象,两个方法的调用者不是同一个,所以两个方法用的不是同一个锁,
两者互不影响,由于one()方法有延时,所以先打印two...
- 第五种情况: 两个静态同步方法,一个ShareResource对象,先打印one还是two
/**
* 线程八锁第五种情况: 两个静态同步方法,一个ShareResource对象,先打印one还是two
* <p>
* 运行结果: 先打印one...,后打印two...
* <p>
* 解释:
* 被static synchronized修饰相当于synchronized(Test8LockDemo.class) { xxx },静态同步方法锁定的是这个类Class对象,也即是整个类的模板都被锁住了。
* 所以两个方法用的是同一个锁对象,所以先打印one...
*/
public class Test8LockDemo {
public static void main(String[] args) throws InterruptedException {
ShareResource shareResource = new ShareResource();
new Thread(() -> shareResource.one()).start();
//这里加一点延迟,为了保证前面的线程先获取执行权.
Thread.sleep(1000);
new Thread(() -> shareResource.two()).start();
}
}
/**
* 共享资源类
*/
class ShareResource {
public static synchronized void one() {
//one()方法睡眠5秒
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one...");
}
public static synchronized void two() {
System.out.println("two...");
}
}
运行结果:
one...
two...
解释:
被static synchronized修饰相当于synchronized(Test8LockDemo.class) { xxx },静态同步方法锁定的是这个类Class对象,也即是整个类的模板都被锁住了。
所以两个方法用的是同一个锁对象,所以先打印one...
- 第六种情况: 两个静态同步方法,两个ShareResource对象,先打印one还是two
/**
* 线程八锁第六种情况: 两个静态同步方法,两个ShareResource对象,先打印one还是two
* <p>
* 运行结果: 先打印one...,后打印two...
* <p>
* 解释:
* 被static synchronized修饰相当于synchronized(Test8LockDemo.class) { xxx },静态同步方法锁定的是这个类Class对象,也即是整个类的模板都被锁住了。
* 因为两个同步方法都被static修饰了,即便用了两个不同的对象调用方法,但是用的模板还是Test8LockDemo.class, 所以两个方法用的是同一个锁对象,所以先打印one...
*/
public class Test8LockDemo {
public static void main(String[] args) throws InterruptedException {
ShareResource shareResource = new ShareResource();
ShareResource shareResource2 = new ShareResource();
new Thread(() -> shareResource.one()).start();
//这里加一点延迟,为了保证前面的线程先获取执行权.
Thread.sleep(1000);
new Thread(() -> shareResource2.two()).start();
}
}
/**
* 共享资源类
*/
class ShareResource {
public static synchronized void one() {
//one()方法睡眠5秒
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one...");
}
public static synchronized void two() {
System.out.println("two...");
}
}
运行结果:
one...
two...
解释:
被static synchronized修饰相当于synchronized(Test8LockDemo.class) { xxx },静态同步方法锁定的是这个类Class对象,也即是整个类的模板都被锁住了。
因为两个同步方法都被static修饰了,即便用了两个不同的对象调用方法,但是用的模板还是Test8LockDemo.class, 所以两个方法用的是同一个锁对象,所以先打印one...
- 第七种情况: 一个静态同步方法,一个普通同步方法,一个ShareResource对象,先打印one还是two
/**
* 线程八锁第七种情况: 一个静态同步方法,一个普通同步方法,一个ShareResource对象,先打印one还是two
* <p>
* 运行结果: 先打印two...,5秒后打印one...
* <p>
* 解释:
* one()方法被synchronized和static修饰,锁定的是ShareResource.class类对象,也即是整个类的模板;
* two()方法被synchronized修饰,锁定的是shareResource这个方法调用者对象;
* 由于两个方法锁的对象不是同一个,所以两个方法用的不是同一个锁,由于one()方法有5秒延迟,所以先打印two...
*/
public class Test8LockDemo {
public static void main(String[] args) throws InterruptedException {
ShareResource shareResource = new ShareResource();
new Thread(() -> shareResource.one()).start();
//这里加一点延迟,为了保证前面的线程先获取执行权.
Thread.sleep(1000);
new Thread(() -> shareResource.two()).start();
}
}
/**
* 共享资源类
*/
class ShareResource {
public static synchronized void one() {
//one()方法睡眠5秒
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one...");
}
public synchronized void two() {
System.out.println("two...");
}
}
运行结果:
two...
one...
解释:
one()方法被synchronized和static修饰,锁定的是ShareResource.class类对象,也即是整个类的模板;
two()方法被synchronized修饰,锁定的是shareResource这个方法调用者对象;
由于两个方法锁的对象不是同一个,所以两个方法用的不是同一个锁,由于one()方法有5秒延迟,所以先打印two...
- 第八种情况: 一个静态同步方法,一个普通同步方法,两个ShareResource对象,先打印one还是two
/**
* 线程八锁第八种情况: 一个静态同步方法,一个普通同步方法,两个ShareResource对象,先打印one还是two
* <p>
* 运行结果: 先打印two...,5秒后打印one...
* <p>
* 解释:
* one()方法被synchronized和static修饰,锁定的是ShareResource.class类对象,也即是整个类的模板;
* two()方法仅仅被synchronized修饰,锁定的是shareResource2这个方法调用者对象;
* 由于两个方法锁的对象不是同一个,所以两个方法用的不是同一个锁,由于one()方法有5秒延迟,所以先打印two...
*/
public class Test8LockDemo {
public static void main(String[] args) throws InterruptedException {
ShareResource shareResource = new ShareResource();
ShareResource shareResource2 = new ShareResource();
new Thread(() -> shareResource.one()).start();
//这里加一点延迟,为了保证前面的线程先获取执行权.
Thread.sleep(1000);
new Thread(() -> shareResource2.two()).start();
}
}
/**
* 共享资源类
*/
class ShareResource {
public static synchronized void one() {
//one()方法睡眠5秒
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one...");
}
public synchronized void two() {
System.out.println("two...");
}
}
运行结果:
two...
one...
解释:
one()方法被synchronized和static修饰,锁定的是ShareResource.class类对象,也即是整个类的模板;
two()方法仅仅被synchronized修饰,锁定的是shareResource2这个方法调用者对象;
由于两个方法锁的对象不是同一个,所以两个方法用的不是同一个锁,由于one()方法有5秒延迟,所以先打印two...
三、总结
- 1. 一个类里面如果有多个synchronized方法,在使用同一个对象调用的前提下,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其他的线程都只能等待,换句话说,某一时刻内,只能有唯一一个线程去访问这些synchronized方法;
- 2. 普通方法后和同步锁无关,执行时无需判断是否拥有锁 ;
- 3. 静态同步方法锁定的是整个类.class对象,普通同步方法锁定的是this,即方法的调用者 ;
- 4. 判断8锁,主要关注是否锁定的同一个对象即可;