目录
简介
通过8个案例来解释说明
案例及总结
简介
阿里规约【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能 锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法。
通过8个案例来解释说明
- 标准访问有a、b两个线程,先打印结果email还是sms
- sendEmail 方法中加入暂停3秒钟,先打印结果 email还是sms
- 添加一个普通的hello方法,先打印结果 email还是hello
- 有两部手机,先打印结果 email还是sms
- 有两个静态同步方法,有1部手机,先打印结果 email还是sms
- 有两个静态同步方法,有2部手机,先打印结果 email还是sms
- 有1个静态同步方法,有一个普通同步方法,有1部手机,先打印结果 email还是sms
- 有1个静态同步方法,有一个普通同步方法,有2部手机,先打印结果 email还是sms
案例及总结
1.标准访问有a、b两个线程,先打印结果email还是sms
import java.util.concurrent.TimeUnit;
class Phone{
public synchronized void sendEmail(){
System.out.println("--------sendEmail");
}
public synchronized void sendSms(){
System.out.println("--------sendSms");
}
}
public class LockTest {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() ->{
phone.sendEmail();
},"a").start();
//暂停毫秒 保证a线程先启动
try { TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(() ->{
phone.sendSms();
},"b").start();
}
}
//结果:
--------sendEmail
--------sendSms
Process finished with exit code 0
2.sendEmail 方法中加入暂停3秒钟,先打印结果 email还是sms
import java.util.concurrent.TimeUnit;
class Phone{
public synchronized void sendEmail(){
try { TimeUnit.MILLISECONDS.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("--------sendEmail");
}
public synchronized void sendSms(){
System.out.println("--------sendSms");
}
}
public class LockTest {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() ->{
phone.sendEmail();
},"a").start();
//暂停毫秒 保证a线程先启动
try { TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(() ->{
phone.sendSms();
},"b").start();
}
}
//结果:
--------sendEmail
--------sendSms
Process finished with exit code 0
总结 1-2:一个对象里面如果有多个synchronized方法,某一个时刻内,只要有一个线程去调用其中的一个synchronized方法了,其他线程都只能等待,换句话说,某一时刻内,只能有唯一的一个线程去访问这些synchronized方法,锁的是当前的对象this,被锁后,其他的线程都不能进入当前对象的其他的synchroniezd方法。
3.添加一个普通的hello方法,先打印结果 email还是hello
import java.util.concurrent.TimeUnit;
class Phone{
public synchronized void sendEmail(){
try { TimeUnit.MILLISECONDS.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("--------sendEmail");
}
public synchronized void sendSms(){
System.out.println("--------sendSms");
}
public void hello(){
System.out.println("-------hello");
}
}
public class LockTest {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() ->{
phone.sendEmail();
},"a").start();
//暂停毫秒 保证a线程先启动
try { TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(() ->{
//phone.sendSms();
phone.hello();
},"b").start();
}
}
//结果:
-------hello
--------sendEmail
Process finished with exit code 0
4.有两部手机,先打印结果 email还是sms
import java.util.concurrent.TimeUnit;
class Phone{
public synchronized void sendEmail(){
try { TimeUnit.MILLISECONDS.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("--------sendEmail");
}
public synchronized void sendSms(){
System.out.println("--------sendSms");
}
public void hello(){
System.out.println("-------hello");
}
}
public class LockTest {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() ->{
phone.sendEmail();
},"a").start();
//暂停毫秒 保证a线程先启动
try { TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(() ->{
//phone.sendSms();
// phone.hello();
phone2.sendSms();
},"b").start();
}
}
//结果
--------sendSms
--------sendEmail
Process finished with exit code 0
总结 3-4:加个普通的方法后发现和同步锁无关 不存在资源争抢的问题
换成两个对象后,不是同一把锁(phone1,phone2)了,不存在资源争抢的问题
5.有两个静态同步方法,有1部手机,先打印结果 email还是sms
import java.util.concurrent.TimeUnit;
class Phone{
public static synchronized void sendEmail(){
try { TimeUnit.MILLISECONDS.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("--------sendEmail");
}
public static synchronized void sendSms(){
System.out.println("--------sendSms");
}
public void hello(){
System.out.println("-------hello");
}
}
public class LockTest {
public static void main(String[] args) {
Phone phone = new Phone();
// Phone phone2 = new Phone();
new Thread(() ->{
phone.sendEmail();
},"a").start();
//暂停毫秒 保证a线程先启动
try { TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(() ->{
phone.sendSms();
// phone.hello();
// phone2.sendSms();
},"b").start();
}
}
//结果
--------sendEmail
--------sendSms
Process finished with exit code 0
6.有两个静态同步方法,有2部手机,先打印结果 email还是sms
import java.util.concurrent.TimeUnit;
class Phone{
public static synchronized void sendEmail(){
try { TimeUnit.MILLISECONDS.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("--------sendEmail");
}
public static synchronized void sendSms(){
System.out.println("--------sendSms");
}
public void hello(){
System.out.println("-------hello");
}
}
public class LockTest {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() ->{
phone.sendEmail();
},"a").start();
//暂停毫秒 保证a线程先启动
try { TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(() ->{
// phone.sendSms();
// phone.hello();
phone2.sendSms();
},"b").start();
}
}
//结果
--------sendEmail
--------sendSms
Process finished with exit code 0
总结 5-6:都换成静态同步方法后,情况又变化,
三种synchronized锁的内容有一些差别
对于普通同步方法:锁的是当前的实例对象,通常是指this,具体的一部手机,所有的普通同步方法用的都是同一把锁 -->实例对象本身
对于静态同步方法:锁的是当前类的class对象,如phone.class唯一的一个模版
对于同步方法块:锁的是synchronized括号内的对象
7.有1个静态同步方法,有一个普通同步方法,有1部手机,先打印结果 email还是sms
import java.util.concurrent.TimeUnit;
class Phone{
public static synchronized void sendEmail(){
try { TimeUnit.MILLISECONDS.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("--------sendEmail");
}
public synchronized void sendSms(){
System.out.println("--------sendSms");
}
public void hello(){
System.out.println("-------hello");
}
}
public class LockTest {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() ->{
phone.sendEmail();
},"a").start();
//暂停毫秒 保证a线程先启动
try { TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(() ->{
phone.sendSms();
// phone.hello();
// phone2.sendSms();
},"b").start();
}
}
//结果
--------sendSms
--------sendEmail
Process finished with exit code 0
8.有1个静态同步方法,有一个普通同步方法,有2部手机,先打印结果 email还是sms
import java.util.concurrent.TimeUnit;
class Phone{
public static synchronized void sendEmail(){
try { TimeUnit.MILLISECONDS.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("--------sendEmail");
}
public synchronized void sendSms(){
System.out.println("--------sendSms");
}
public void hello(){
System.out.println("-------hello");
}
}
public class LockTest {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() ->{
phone.sendEmail();
},"a").start();
//暂停毫秒 保证a线程先启动
try { TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(() ->{
// phone.sendSms();
// phone.hello();
phone2.sendSms();
},"b").start();
}
}
//结果
--------sendSms
--------sendEmail
Process finished with exit code 0
总结 7-8:当一个线程试图访问同步代码时,它必须得到锁,正常退出或抛出异常必须释放锁。
1.所有的同步普通同步方法用的都是同一把锁-->实例对象本身,就是new出来的具体实例对象本身,本类this。也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他同步方法必须等待获取锁的方法释放锁后才能获取锁。
2.所有的静态同步方法用的也是同一把锁-->类对象本身,就是我们所说的唯一模板class,
具体实例对象this和唯一模版class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会竞态条件的。
3.但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁 。
如果还是没有整明白类锁和对象锁 一图解千愁
简单说下:程序中Phone phone1 = new Phone();phone1是在堆区 ,就是对象锁在的地方
Phone Class类模板是在方法区,所以说对象锁和类锁两个不同的对象,不存在竞太的关系。
如有不对的地方欢迎留言指正