Java多线程之8Lock问题解析

Java多线程之8Lock问题解析


本文目录

1. 8Lock实例:
  1. 标准访问的时候,请问先打印邮件还是短信?
  2. sendEmail方法暂停4秒钟,请问先打印邮件还是短信?
  3. 新增Hello普通方法,请问先打印邮件还是Hello?
  4. 两部手机,请问先打印邮件还是短信?
  5. 两个静态同步方法,同1部手机 ,请问先打印邮件还是短信?
  6. 两个静态同步方法,有2部手机 ,请问先打印邮件还是短信?
  7. 1个静态同步方法,1个普通同步方法,有1部手机 ,请问先打印邮件还是短信?
  8. 1个静态同步方法,1个普通同步方法,有2部手机 ,请问先打印邮件还是短信?
2. 8Lock总结
3. 补充:当前类的Class对象和当前类的实例对象分别是什么?

8Lock实例:


1. 标准访问的时候,请问先打印邮件还是短信?


代码:

//资源类
class Phone{
	public  synchronized void sendEmail() {
		System.out.println("sendEmail----------");
	}
	
	public synchronized void getSMS() {
		System.out.println("----------getSMS");
	}
}
//主方法
public class Lock_8 {

	public static void main(String[] args) {
		Phone p=new Phone();
		
		new Thread(() ->{
			p.sendEmail();
		},"A").start();
		
		new Thread(() ->{
			p.getSMS();;
		},"B").start();
	}
}

结果:
在这里插入图片描述
在这里插入图片描述


解析:

  • 不知道,因为线程谁抢到了谁执行,一般情况下是A执行

2. sendEmail方法暂停4秒钟,请问先打印邮件还是短信?


代码:

//资源类
class Phone{
	public  synchronized void sendEmail() {
		Thread.sleep(4000);
		System.out.println("sendEmail----------");
	}
	
	public synchronized void getSMS() {
		System.out.println("----------getSMS");
	}
}
//主方法
public class Lock_8 {

	public static void main(String[] args) throws Exception {
		Phone p=new Phone();
		
		new Thread(() ->{
			p.sendEmail();
		},"A").start();
		//强制让线程A先执行
		Thread.sleep(200);
		
		new Thread(() ->{
			p.getSMS();;
		},"B").start();
	}
}



结果:
在这里插入图片描述


解析:

  • 4秒后A先执行,紧接着B执行。
  • 因为休眠main线程之前A已经启动了,时间足够A运行,A会锁住资源类的入口(也就是对象)。所以A先执行。

3. 新增Hello普通方法,请问先打印邮件还是Hello?


代码:

//资源类
class Phone{
	public  synchronized void sendEmail() throws Exception {
		TimeUnit.SECONDS.sleep(4);
		System.out.println("sendEmail----------");
	}
	
	public synchronized void getSMS() {
		System.out.println("----------getSMS");
	}
	
	public void sayHello() {
		System.out.println("---hello---");
	}
}
//主方法
public class Lock_8 {

	public static void main(String[] args) throws Exception {
		Phone p=new Phone();
		
		new Thread(() ->{
			try {
				p.sendEmail();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"A").start();
		//强制让线程A先执行
		Thread.sleep(200);
		
		new Thread(() ->{
			p.sayHello();
		},"B").start();
	}
}

结果:

在这里插入图片描述


解析:

  • 先打印hello,虽然sendEmail锁住了资源类的入口(对象),因为sayHello未上锁,所以sayHello方法可以进入资源类。
  • 但是Thread.sleep()锁住了main线程,也就是hello打印出来的延迟时间就是休眠的设定时间。
加个普通方法后发现和同步锁无关

4. 两部手机,请问先打印邮件还是短信?


代码:

//资源类
class Phone{
	public  synchronized void sendEmail() throws Exception {
		TimeUnit.SECONDS.sleep(4);
		System.out.println("sendEmail----------");
	}
	
	public synchronized void getSMS() {
		System.out.println("----------getSMS");
	}
	
	public void sayHello() {
		System.out.println("---hello---");
	}
}
//主方法
public class Lock_8 {
	public static void main(String[] args) throws Exception {
		Phone p1=new Phone();
		Phone p2=new Phone();
		new Thread(() ->{
			try {
				p1.sendEmail();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"A").start();
		//强制让线程A先执行
		Thread.sleep(2000);
		
		new Thread(() ->{
			p2.getSMS();
		},"B").start();
	}
}



结果:

在这里插入图片描述


解析:

  • 先打印SMS,因为锁的是资源类的入口,也就是对象,既然不是同一个对象,那肯定锁不住。并且线程B的执行和main线程的休眠时间相关。
换成两个对象后,不是同一把锁了,情况立刻变化。

5. 两个静态同步方法,同1部手机 ,请问先打印邮件还是短信?


代码:

//资源类
class Phone{
	public static synchronized void sendEmail() throws Exception {
		TimeUnit.SECONDS.sleep(4);
		System.out.println("sendEmail----------");
	}
	
	public static synchronized void getSMS() {
		System.out.println("----------getSMS");
	}
	
	public void sayHello() {
		System.out.println("---hello---");
	}
}
//主方法
public class Lock_8 {

	public static void main(String[] args) throws Exception {
		Phone p=new Phone();
		
		
		new Thread(() ->{
			try {
				p.sendEmail();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"A").start();
		//强制让线程A先执行
		Thread.sleep(5000);
		
		new Thread(() ->{
			p.getSMS();
		},"B").start();
	}
}




结果:

在这里插入图片描述


解析:

  • 先打印sendEmali,然后打印getSMS,因为静态同步锁,锁的是Phone.Class,所以B线程在A线程执行完之前进不去。
  • synchronized实现同步的基础:Java中的每一个对象都可以作为锁。
具体表现为以下3种形式。
1. 对于普通同步方法,锁是当前实例对象。
2. 对于静态同步方法,锁是当前类的Class对象。
3. 对于同步方法块,锁是Synchonized括号里配置的对象。

6. 两个静态同步方法,有2部手机 ,请问先打印邮件还是短信?


代码:

//资源类
class Phone{
	public static synchronized void sendEmail() throws Exception {
		TimeUnit.SECONDS.sleep(4);
		System.out.println("sendEmail----------");
	}
	
	public static synchronized void getSMS() {
		System.out.println("----------getSMS");
	}
	
	public void sayHello() {
		System.out.println("---hello---");
	}
}
//主方法
public class Lock_8 {

	public static void main(String[] args) throws Exception {
		Phone p1=new Phone();
		Phone p2=new Phone();
		
		
		new Thread(() ->{
			try {
				p1.sendEmail();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"A").start();
		//强制让线程A先执行
		Thread.sleep(5000);
		
		new Thread(() ->{
			p2.getSMS();
		},"B").start();
	}
}



结果:

在这里插入图片描述


解析:

  • 看似和5不一样,其实是一回事,虽然两个对象,因为强制A先执行,所以A会锁住资源类,因此先打印sendEmail。

7. 1个静态同步方法,1个普通同步方法,有1部手机 ,请问先打印邮件还是短信?


代码:

//资源类
class Phone{
    public static synchronized void sendEmail() throws Exception {
        TimeUnit.SECONDS.sleep(2);
        System.out.println("sendEmail----------");
    }

    public synchronized void getSMS() {
        System.out.println("----------getSMS");
    }

    public void sayHello() {
        System.out.println("---hello---");
    }
}
//主方法
public class Lock_8 {

    public static void main(String[] args) throws Exception {
        Phone p=new Phone();



        new Thread(() ->{
            try {
                p.sendEmail();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        },"A").start();
        //强制让线程A先执行
        Thread.sleep(1000);

        new Thread(() ->{
            p.getSMS();
        },"B").start();
    }
}

结果:

在这里插入图片描述


解析:

  • static方法锁锁的不是资源类,锁的是类对象,也就是说,不管new了几个,静态同步方法的类对象都是一个;而普通同步方法锁住的是new出来的对象。
  • 所有的静态同步方法用的也是同一把锁——类对象本身,
    这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
    但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,
    而不管是否是同一个实例对象。

8. 1个静态同步方法,1个普通同步方法,有2部手机 ,请问先打印邮件还是短信?


代码:

//资源类
class Phone{
	public static synchronized void sendEmail() throws Exception {
		TimeUnit.SECONDS.sleep(4);
		System.out.println("sendEmail----------");
	}
	
	public synchronized void getSMS() {
		System.out.println("----------getSMS");
	}
	
	public void sayHello() {
		System.out.println("---hello---");
	}
}
//主方法
public class Lock_8 {

	public static void main(String[] args) throws Exception {
		Phone p1=new Phone();
		Phone p2=new Phone();
		
		
		
		new Thread(() ->{
			try {
				p1.sendEmail();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"A").start();
		//强制让线程A先执行
		Thread.sleep(1000);
		
		new Thread(() ->{
			p2.getSMS();
		},"B").start();
	}
}


结果:

在这里插入图片描述


解析:

  • P1锁住的不是资源类,而是类对象。线程1锁的是类对象,线程2锁的是实例对象,虽然看似是一个对象,然而两者不具备竞态条件,因此修改main休眠时间之后就会发现getSMS先执行。

8Lock总结

  1. 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
  2. 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
  3. 加个普通方法后发现和同步锁无关
  4. 换成两个对象后,不是同一把锁了,情况立刻变化。
  5. 都换成静态同步方法后,情况又变化
  6. 所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
  7. 所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
  8. 一个静态同步方法 一个普通同步方法,一个非同步方法,那么三者是都不具备竞态条件的。

3. 补充:当前类的Class对象和当前类的实例对象分别是什么?

实例:

  • 简单理解,就是new,就是对类的实例化,创建这个类对应的实际对象,类只是对事物的描述,而实例化就相当于为这个描述新开辟了一块内存,可以改变这块区域里的各种属性(成员变量),当然,也可以实例化多块区域,只是不同的对象而已。

Class:

  • 注意这里C大写了,与类概念区分开,在java里,Class是一个实实在在的类,在包 java.lang 下,有这样一个Class.java文件,它跟我们自己定义的类一样,是一个实实在在的类,Class对象就是这个Class类的实例了。在Java里,所有的类的根源都是Object类,而Class也不例外,它是继承自Object的一个特殊的类,它内部可以记录类的成员、接口等信息,也就是在Java里,Class是一个用来表示类的类。(o(∩_∩)o 有点绕啊,抓住关键一点,Class是一个实实在在的类,可以为它创建实例,也就是本文后面提到的Class对象,也看叫做Class实例)。

java提供了下面几种获取到类的Class对象的方法:

1) 利用对象实例调用getClass()方法获取该对象的Class实例;
2) 使用Class类的静态方法forName(“包名+类名”),用类的名字获取一个Class实例
3) 运用 类名.class 的方式来获取Class实例;

我们知道java世界是运行在JVM之上的,我们编写的类代码,在经过编译器编译之后,会为每个类生成对应的.class文件,这个就是JVM可以加载执行的字节码。运行时期间,当我们需要实例化任何一个类时,JVM会首先尝试看看在内存中是否有这个类,如果有,那么会直接创建类实例;如果没有,那么就会根据类名去加载这个类,当加载一个类,或者当加载器(class loader)的defineClass()被JVM调用,便会为这个类产生一个Class对象(一个Class类的实例),用来表达这个类,该类的所有实例都共同拥有着这个Class对象,而且是唯一的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值