深刻理解锁究竟是什么,锁的对象究竟是谁
8个锁的问题,两两分组,问题,答案,代码皆在下面代码块中,如有问题后续补充
package Lock8;
import java.util.concurrent.TimeUnit;
/**
* 8锁,就是关于锁的8个问题:
* 1、标准情况下,两个线程先打印哪一个?---->先打印sendSms
* 2、sendSms延迟4秒后,两个线程哪个线程先打印?---->先打印sendSms,因为synchronized锁锁的是资源对象
*/
public class Test01 {
public static void main(String[] args) {
// 资源类
Phone phone = new Phone();
// 创建并开启线程
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
// synchronized 锁的对象是方法的调用者,因此,资源对象被锁住了,线程A与线程B所使用的资源对象为同一个phone
// 两个方法用的是同一个锁,谁先拿到谁执行
public synchronized void sendSms(){
//--------------------------- 2 --------------------------
// 让sendSms加4秒延迟
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//--------------------------- 2 --------------------------
System.out.println("send a sms");
}
public synchronized void call(){
System.out.println("call");
}
}
package Lock8;
import java.util.concurrent.TimeUnit;
/**
* 3、增加了一个普通方法后,是先执行发短信还是执行hello--->hello 先输出,因为hello并不是同步方法,不受锁的影响,而sendSms有睡眠延迟
* 4、两个对象,两个同步方法,先执行发短信还是执行打电话?----> call 先输出,这里有两个不同的对象,也就意味着有两把锁,A与B拿的锁不一样
*/
public class Test02 {
public static void main(String[] args) {
// ------------------------- 3 -----------------------------------
// // 资源类
// Phone2 phone = new Phone2();
// // 创建并开启线程
// new Thread(()->{
// phone.sendSms();
// },"A").start();
//
// try {
// TimeUnit.SECONDS.sleep(1);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// new Thread(()->{
// phone.hello();
// },"B").start();
// ------------------------- 3 -----------------------------------
// ------------------------- 4 -----------------------------------
// 资源类
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
// 创建并开启线程
new Thread(()->{
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
phone2.call();
},"B").start();
// ------------------------- 4 -----------------------------------
}
}
class Phone2{
// synchronized 锁的对象是方法的调用者,因此,资源对象被锁住了,线程A与线程B所使用的资源对象为同一个phone
// 两个方法用的是同一个锁,谁先拿到谁执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("send a sms");
}
public synchronized void call(){
System.out.println("call");
}
// 这里没有锁,不是同步方法,不受锁的影响
public void hello(){
System.out.println("this function is saying hello!");
}
}
package Lock8;
import java.util.concurrent.TimeUnit;
/**
*5、增加两个静态的同步方法,只有一个对象,先打印哪一个?----->
* 先发短信,因为当将方法声明为static时,则该方法全局唯一,与Class一同加载,换句话说,该锁锁的是类,而非方法
*6、两个对象,增加两个静态的同步方法,先打印哪一个?----->
* 先发短信,同样的,因为static的缘故,synchronized锁住的是类,而类只有一个,因此实际上只有一把锁
*/
public class Test03 {
public static void main(String[] args) {
// ------------------------- 5 -----------------------------------
// // 资源类
// Phone3 phone = new Phone3();
//
// // 创建并开启线程
// new Thread(()->{
// phone.sendSms();
// },"A").start();
//
// try {
// TimeUnit.SECONDS.sleep(1);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// new Thread(()->{
// phone.call();
// },"B").start();
// ------------------------- 5 -----------------------------------
// ------------------------- 6 -----------------------------------
// 资源类
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
// 创建并开启线程
new Thread(()->{
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
phone2.call();
},"B").start();
// ------------------------- 6 -----------------------------------
}
}
// Phone3 唯一的一个class对象
// 在Java的世界里,万物皆对象,类也不例外,类有两个对象,实例对象以及Class对象
// Class对象包含了与类有关的信息,实例对象就是通过Class对象创建的
class Phone3{
// synchronized 锁的对象是方法的调用者
// static 静态方法
// 类一加载就有了,锁的是Class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("send a sms");
}
public static synchronized void call(){
System.out.println("call");
}
}
package Lock8;
import java.util.concurrent.TimeUnit;
/**
*7、 一个静态同步方法,一个普通同步方法,一个对象,谁先打印?----->
* 打电话先打印,发短信锁的是Class类模板,打电话锁的是对象,锁的对象不一样,因此有两把锁
*8、 一个静态同步方法,一个普通同步方法,两个对象,谁先打印?----->
* 打电话先打印,发短信锁的是Class类模板,打电话锁的是对象,锁的对象不一样,因此有两把锁
*/
public class Test04 {
public static void main(String[] args) {
// ------------------------- 7 -----------------------------------
// // 资源类
// Phone4 phone = new Phone4();
//
// // 创建并开启线程
// new Thread(()->{
// phone.sendSms();
// },"A").start();
//
// try {
// TimeUnit.SECONDS.sleep(1);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// new Thread(()->{
// phone.call();
// },"B").start();
// ------------------------- 7 -----------------------------------
// ------------------------- 8 -----------------------------------
// 资源类
Phone4 phone1= new Phone4();
Phone4 phone2 = new Phone4();
// 创建并开启线程
new Thread(()->{
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
phone2.call();
},"B").start();
// ------------------------- 8 -----------------------------------
}
}
class Phone4{
// 静态同步方法 锁的是Class类模板
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("send a sms");
}
// 普通同步方法 锁的是调用者
public synchronized void call(){
System.out.println("call");
}
}
小结
new 出来的对象,是一个实例对象,实例可以有很多个,因此synchronized锁只是锁在实例对象上,如果两个线程持有的实例对象资源不同,则两个线程独立,并不会被对方的锁干扰
static 则表名该方法与Class同步产生,因此,static是在Class对象中的,如果synchronized锁锁住了static方法,则表明锁住了整个Class对象,而因为Class对象唯一(所有的实例对象都通过Class对象创建的)因此,无论后续创建了多少个实例对象资源,最终只要某一线程持有住某一静态同步方法的锁,则其他静态同步方法一律无法使用。
而同时,因为Class对象和实例对象本质上是两个对象(虽然实例对象是通过Class对象创建而来的),因此某一线程持有某一静态同步方法的锁,也无法影响其他普通同步方法
总而言之:
普通同步锁实例,静态同步锁模板,
即使再多实例在,静态之间统一管
静态普通不同属,相互之间无法管