一、线程的8锁问题
前提:8锁问题的讨论是建立在使用Thread.sleep(200)后,假设A线程先于B线程被CPU调度的前提下。
1.1 标准访问,请问先打印邮件还是短信?
class Phone{
public synchronized void sendEmail() throws Exception{
System.out.println("----------sendEmail");
}
public synchronized void sendSMS() throws Exception{
System.out.println("----------sendSMS");
}
}
/**
* 题目:多线程8锁
* 1. 标准访问,请问先打印邮件还是短信?
*/
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
// 这里使线程先睡眠200毫秒 模拟出A线程先于B线程被CPU调用
Thread.sleep(200);
new Thread(()->{
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
- 如果不加Thread.sleep(200) 则不确定是先打印发邮件还是先发短信,虽然打印邮件比打印短信线程先进入就绪状态,但是cpu调度其顺序不确定。
- 加了Thread.sleep(200)很大概率先发送邮件再打印短信,200毫秒基本cpu已经调用了线程A
1.2邮件方法暂定4秒钟,请问先打印邮件还是短信?
class Phone{
public synchronized void sendEmail() throws Exception{
TimeUnit.SECONDS.sleep(4);
System.out.println("----------发送邮件");
}
public synchronized void sendSMS() throws Exception{
System.out.println("----------发送短信");
}
}
...
// Lock8类同上
先发邮件后发短信,sleep只是释放了cpu的调度时间并没有释放锁对象,B线程只有等A线程释放了当前对象锁才能执行。
1.3 新增一个普通方法hello(),请问先打印邮件还是hello?
public class Lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
// 这里使线程先睡眠200毫秒 模拟出A线程先于B线程被CPU调用
Thread.sleep(200);
new Thread(()->{
try {
// phone.sendSMS();
phone.hello();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
class Phone{
public synchronized void sendEmail() throws Exception{
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"----------发送邮件");
}
public synchronized void sendSMS() throws Exception{
System.out.println(Thread.currentThread().getName()+"----------发送短信");
}
public void hello(){
System.out.println(Thread.currentThread().getName()+"-----------hello");
}
}
B-----------hello
A----------发送邮件
先打印hello在发送邮件
A线程被CPU调度,A发送邮件前睡1秒无释放锁对象即phone对象锁,A线程睡200毫秒后B线程进入就绪态被CPU调度,因为
hello方法没有锁,无需获取锁也可执行
,所以打印了hello,等到1秒后A线程睡醒打印发送邮件。
1.4两部手机请问先打印邮件还是短信?
public class Lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
// 这里使线程先睡眠200毫秒 模拟出A线程先于B线程被CPU调用
Thread.sleep(200);
new Thread(()->{
try {
phone2.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
class Phone{
public synchronized void sendEmail() throws Exception{
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"----------发送邮件");
}
public synchronized void sendSMS() throws Exception{
System.out.println(Thread.currentThread().getName()+"----------发送短信");
}
public void hello(){
System.out.println(Thread.currentThread().getName()+"-----------hello");
}
}
//输出
//B----------发送短信
//A----------发送邮件
解释:
synchronized是对象锁,A先被CPU调度、发送邮件先睡一秒,此时A线程锁的是phone对象,phone2对象并未上锁。200毫秒后B线程被CPU调度,发送短信。A线程1秒睡醒后发送邮件。
1.5两个静态同步方法,同一部手机,请问先打印邮件还是发送短信?
public class Lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
// 这里使线程先睡眠200毫秒 模拟出A线程先于B线程被CPU调用
Thread.sleep(200);
new Thread(()->{
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
class Phone{
public static synchronized void sendEmail() throws Exception{
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"----------发送邮件");
}
public static synchronized void sendSMS() throws Exception{
System.out.println(Thread.currentThread().getName()+"----------发送短信");
}
}
A----------发送邮件
B----------发送短信
A线程被CPU调度,发送邮件前睡1秒,因为加了statics所以此时
睡的时候锁的是类模板
,这里也就是Phone,200毫秒后B线程被CPU调度,此时因为Phone类上锁了所以等待,1秒后A线程睡醒发送邮件释放Phone类锁,然后B线程再发送短信
1.6 两个静态同步方法,2部手机,请问先打印邮件还是发送短信?
public class Lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
// 这里使线程先睡眠200毫秒 模拟出A线程先于B线程被CPU调用
Thread.sleep(200);
new Thread(()->{
try {
phone2.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
class Phone{
public static synchronized void sendEmail() throws Exception{
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"----------发送邮件");
}
public static synchronized void sendSMS() throws Exception{
System.out.println(Thread.currentThread().getName()+"----------发送短信");
}
}
A----------发送邮件
B----------发送短信
phone和phone2的类模板都是Phone,线程A被CPU调度后,发送邮件前睡1秒锁上类模板,200毫秒后B线程进入就绪态开始被CPU调度,但是Phone类模板被锁,所以B线程只有等A线程释放类模板锁。
1.7 1个普通同步方法,1个静态同步方法,1部手机,请问先打印邮件还是短信?
public class Lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
// 这里使线程先睡眠200毫秒 模拟出A线程先于B线程被CPU调用
Thread.sleep(200);
new Thread(()->{
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
class Phone{
public static synchronized void sendEmail() throws Exception{
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"----------发送邮件");
}
public synchronized void sendSMS() throws Exception{
System.out.println(Thread.currentThread().getName()+"----------发送短信");
}
public void hello(){
System.out.println(Thread.currentThread().getName()+"-----------hello");
}
}
B----------发送短信
A----------发送邮件
类模板锁和具体的对象锁无关
A线程被调度,发送邮件前睡1秒锁住创建类的类模板,200毫秒后B线程被调度,因为B发送短信的方法是具体对象的,而且类模板锁和具体的对象锁又无关所以此时可以发送短信,然后A线程睡1秒后继续执行。
1.8 1个普通同步方法,1个静态同步方法,2部手机,请问先打印邮件还是短信?
public class Lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
// 这里使线程先睡眠200毫秒 模拟出A线程先于B线程被CPU调用
Thread.sleep(200);
new Thread(()->{
try {
phone2.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
class Phone{
public static synchronized void sendEmail() throws Exception{
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"----------发送邮件");
}
public synchronized void sendSMS() throws Exception{
System.out.println(Thread.currentThread().getName()+"----------发送短信");
}
public void hello(){
System.out.println(Thread.currentThread().getName()+"-----------hello");
}
}
B----------发送短信
A----------发送邮件
原因同1.7.1