理解“锁”
Java多线程最难理解的锁的机制,下面会通过实例代码来理解
实例一
同一对象调用同步块里的方法
package com.leo.lock8;
import java.util.concurrent.TimeUnit;
/**
* @description:
* @author: Leo
* @createDate: 2020/2/27
* @version: 1.0
*/
public class Test01
{
public static void main(String[] args)
{
Phone phone = new Phone();
new Thread(() ->
{
phone.sendMsg();
},"A").start();
try
{
TimeUnit.SECONDS.sleep(1);//延时1秒
}
catch (InterruptedException e)
{
e.printStackTrace();
}
new Thread(()->{phone.call();},"B").start();
}
}
class Phone
{
public synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);//延时4秒
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发送消息!");
}
public synchronized void call()
{
System.out.println("打电话!");
}
}
通过创建一个Phone对象,里面定义两个同步方法,然后创建A/B两个线程来调用下面的方法,判断这两个方法谁先调用!
synchronized同步方法,谁调用他就锁谁,这里我们看到只有一个phone对象去调用它,所以根据代码的编写来看,运行4秒后会先执行发短信,随机执行打电话的方法;因为这两个方法是同一个锁,谁写被拿到谁执行。
实例二
package com.leo.lock8;
import java.util.concurrent.TimeUnit;
/**
* @description:
* @author: Leo
* @createDate: 2020/2/27
* @version: 1.0
*/
public class Test02
{
//发短信和hello谁会先执行?
public static void main(String[] args)
{
Phone2 phone = new Phone2();
new Thread(() ->
{
phone.sendMsg();
}, "A").start();
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
new Thread(() ->
{
phone.hello();
}, "B").start();
}
}
class Phone2
{
public synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发送消息!");
}
public void hello(){
System.out.println("hello!");
}
}
这里Phone2类里有一个发短信的同步方法,还有一个hello普通方法,现在依旧是两个线程,调用发消息和打印hello,谁会先被打印?这里是hello先被打印,因为hello方法并没有被锁,所以在msg方法延迟4秒的时候他不需要等待,只需要按照上面代码定义的延迟两秒打印hello,然后到了第四秒打印消息。
实例三
两个对象执行同步方法
package com.leo.lock8;
import java.util.concurrent.TimeUnit;
/**
* @description:
* @author: Leo
* @createDate: 2020/2/27
* @version: 1.0
*/
public class Test02
{
//发短信和hello谁会先执行?
public static void main(String[] args)
{
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(() ->
{
phone1.sendMsg();
}, "A").start();
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
new Thread(() ->
{
phone2.call();
}, "B").start();
}
}
class Phone2
{
public synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发送消息!");
}
public synchronized void call()
{
System.out.println("打电话!");
}
}
这里有两个不同的对象,依据谁调用就锁谁的原则,现在phone1调用就锁phone1,这就是一把锁。phone2调用就锁phone2这是另一把锁。所以现在有了两把锁。所以他锁的肯定不是同一个对象,代码就这么走了,phone1去调用发短信的方法,发短信延迟4秒执行,然后phone2是另一把锁,所以phone2并不需要等phone1执行完,他只是等待了2秒就把打电话打印了出来;然后到了第四秒打印了发消息。
实例四
单个对象调用静态同步方法
package com.leo.lock8;
import java.util.concurrent.TimeUnit;
/**
* @description:
* @author: Leo
* @createDate: 2020/2/27
* @version: 1.0
*/
public class Test03
{
//
public static void main(String[] args)
{
//现在有两个对象,谁会先执行?
Phone3 phone = new Phone3();
new Thread(() ->
{
phone.sendMsg();
}, "A").start();
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
new Thread(() ->
{
phone.call();
}, "B").start();
}
}
class Phone3
{
public static synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发送消息!");
}
public static synchronized void call()
{
System.out.println("打电话!");
}
}
加了static关键字之后,概念就完全不一样了,static是静态方法,在类一加载的时候就有了,所以他锁的是class对象,这里两个方法都被static所修饰,所以他们锁的是同一个对象,所以先调用的消息先被打印,然后打印电话。
实例五
两个对象调用静态同步方法
package com.leo.lock8;
import java.util.concurrent.TimeUnit;
/**
* @description:
* @author: Leo
* @createDate: 2020/2/27
* @version: 1.0
*/
public class Test03
{
//
public static void main(String[] args)
{
//现在有两个对象,谁会先执行?
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
new Thread(() ->
{
phone1.sendMsg();
}, "A").start();
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
new Thread(() ->
{
phone2.call();
}, "B").start();
}
}
class Phone3
{
public static synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发送消息!");
}
public static synchronized void call()
{
System.out.println("打电话!");
}
}
现在有多了一个对象来调用同步块里的方法,那么谁先打印呢?很显然执行结果依旧不变,因为上面提过static同步块锁的是class对象,phone3虽然被实例两次,但是class对象只有一个。
实例六
单个对象调用静态同步方法与普通同步方法
package com.leo.lock8;
import java.util.concurrent.TimeUnit;
/**
* @description:
* @author: Leo
* @createDate: 2020/2/27
* @version: 1.0
*/
public class Test04
{
//
public static void main(String[] args)
{
//现在有两个对象,谁会先执行?
Phone4 phone = new Phone4();
new Thread(() ->
{
phone.sendMsg();
}, "A").start();
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
new Thread(() ->
{
phone.call();
}, "B").start();
}
}
class Phone4
{
public static synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发送消息!");
}
public synchronized void call()
{
System.out.println("打电话!");
}
}
到了这就很了然了,摆明了这是两把锁,静态方法块锁的class对象,普通同步方法锁的是调用者。所以后面的打电话不需要去等待前面的方法。所以打电话先执行。
实例七
两个对象调用静态同步方法与普通同步方法
package com.leo.lock8;
import java.util.concurrent.TimeUnit;
/**
* @description:
* @author: Leo
* @createDate: 2020/2/27
* @version: 1.0
*/
public class Test04
{
//
public static void main(String[] args)
{
//现在有两个对象,谁会先执行?
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(() ->
{
phone1.sendMsg();
}, "A").start();
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
new Thread(() ->
{
phone2.call();
}, "B").start();
}
}
class Phone4
{
public static synchronized void sendMsg()
{
try
{
TimeUnit.SECONDS.sleep(4);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("发送消息!");
}
public synchronized void call()
{
System.out.println("打电话!");
}
}
现在我们就知道套路是什么了,我们研究锁的执行,就先看他们锁的是不是同一个对象,看这里跟上面一样,锁的依然不是一个对象,所以打电话不用等就执行了,执行结果跟上面一样。
通过这些例子对多线程锁的理解一定会更为透彻。我本人是看了两遍,加上码这篇文章又复习了一遍。一共是三遍,当你看到狂总问你的题目,你是心里完全有正确思路的时候,就说明你已经掌握了,如果三遍还不行那就五遍八遍,书读百遍其义自见。
Author By 朝花不迟暮
Learn From 狂神说
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16462.html