多线程里面的静态同步锁和普通同步锁的区别(八锁现象)
首先挂上总结,这是别人总结的,觉得不错,后面的代码及总结是我自己的,大家看哪个容易懂就参考哪个
一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化。
都换成静态同步方法后,情况又变化
所有的非静态同步方法用的都是同一把锁——实例对象本身,
synchronized实现同步的基础:Java中的每一个对象都可以作为锁。
具体表现为以下3种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象。
当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,
可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,
所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁——类对象本身,
这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,
而不管是同一个实例对象的静态同步方法之间,
还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
首先,一旦sychronized锁修饰之后,锁住的就是当前实例的对象,那么当前对象的其他sychronized锁执行时候就得需要等待,只有当那个锁释放之后才能执行
1、sychronized锁修饰,全部上锁
package com.zha.thread.lock8;
import java.util.concurrent.TimeUnit;
/**
* @author zhazhalin
* @version 1.0
* @date 2021/3/14 12:35
*/
public class Lock1 {
public static void main(String[] args) {
Phone phone=new Phone();
//首先会执行发短信,因为sychronized锁会锁对象,程序自上而下执行,发短信首先获得锁,所以必须等它执行完,
//其他的才能获得锁
new Thread(()->{phone.sendMsg();}).start(); //下面两个Synchronized锁
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{phone.call();}).start();
}
}
class Phone{
public synchronized void sendMsg(){
System.out.println("发短信!!!");
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void call(){
System.out.println("打电话!!!");
}
}
运行结果:
发短信!!!
打电话!!!
2、sychronized锁,部分加锁
当sychronied锁锁住对象时候,由于一个方法没有加sychronized锁,所以在线程一执行的时候不需要等线程一执行完,可以直接运行。
package com.zha.thread.lock8;
import java.util.concurrent.TimeUnit;
/**
* @author zhazhalin
* @version 1.0
* @date 2021/3/14 14:36
*/
public class Lock2 {
public static void main(String[] args) {
Phone1 phone=new Phone1();
/*由于发邮箱没有上sychronized锁,所以在执行下面代码时候,第一个线程会先执行,方法里面等待4秒
* 然后在一秒后执行下一个线程,那么此时就会出现先发邮箱,后发短信的现象*/
new Thread(()->{phone.sendMsg();},"A").start(); //下面两个Synchronized锁
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{phone.sendEmail();},"B").start();
}
}
class Phone1{
//sychronized锁住的是方法的调用者,锁住的是对象
public synchronized void sendMsg(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信!!!");
}
public synchronized void call(){
System.out.println("打电话!!!");
}
//普通方法当执行的时候不会被锁住
public void sendEmail(){
System.out.println("发邮箱!!!");
}
}
运行结果:
发邮箱!!!
发短信!!!
3、sychronized锁住不同的对象
当sychronized锁住不同的对象的时候,两个对象间互不干扰,这时候你会发现,上面的例子就变成了先打电话了。具体详解看代码解释
package com.zha.thread.lock8;
import java.util.concurrent.TimeUnit;
/**
* @author zhazhalin
* @version 1.0
* @date 2021/3/14 14:44
*/
public class Lock3 {
public static void main(String[] args) {
Phone2 phone1=new Phone2();
Phone2 phone2=new Phone2();
/*首先打电话,因为sychronized锁锁住的是对象,现在是两个不同的对象,所以下面两个线程分别执行,不会因为
* 其中一个锁没有释放而等待,所以先执行线程1,然后等待一秒执行线程2,,所以先打印打电话*/
new Thread(()->{phone1.sendMsg();},"A").start(); //下面两个Synchronized锁
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{phone2.call();},"B").start();
}
}
class Phone2{
//sychronized锁住的是方法的调用者,锁住的是对象
public synchronized void sendMsg(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信!!!");
}
public synchronized void call(){
System.out.println("打电话!!!");
}
//普通方法当执行的时候不会被锁住
public void sendEmail(){
System.out.println("发邮箱!!!");
}
}
打电话!!!
发短信!!!
4、static修饰的sychronized锁
当static修饰同步锁时候,会锁住字节码文件,就是锁住类,那么此时不管创建了几个对象,只要static修饰的方法,都需要等待锁释放才能执加锁执行
package com.zha.thread.lock8;
import java.util.concurrent.TimeUnit;
/**
* @author zhazhalin
* @version 1.0
* @date 2021/3/14 14:51
*/
public class Lock3Static {
public static void main(String[] args) {
Phone3 phone1=new Phone3();
Phone3 phone2=new Phone3();
/*加上static之后锁住的就是整个类,那么一个线程开启,就会直接锁住类,只有等该锁释放之后才能继续对其进行加锁
* 所以是先执行A线程,然后等待线程1执行完之后才会继续执行线程2*/
new Thread(()->{phone1.sendMsg();},"A").start(); //下面两个Synchronized锁
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{phone2.call();},"B").start();
}
}
class Phone3{
//sychronized锁住的是方法的调用者,锁住的是对象,但是如果加上static之后锁住的就是类
// Class<Phone3> phone3Class = Phone3.class;
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("打电话!!!");
}
//普通方法当执行的时候不会被锁住
public void sendEmail(){
System.out.println("发邮箱!!!");
}
}
运行结果:
发短信!!!
打电话!!!
5、static修饰的部分sychronized锁(两个对象)
当static修饰一个类之后,如果一个方法只是加了一个普通的同步锁,那么不会受到static的影响。
package com.zha.thread.lock8;
import java.util.concurrent.TimeUnit;
/**
* @author zhazhalin
* @version 1.0
* @date 2021/3/14 15:02
*/
public class Lock4 {
public static void main(String[] args) {
Phone4 phone1=new Phone4();
Phone4 phone2=new Phone4();
/*加上static之后锁住的就是整个类,那么一个线程开启,就会直接锁住类,只有等该锁释放之后才能继续对其进行加锁
* 所以是先执行A线程,然后等待线程1执行完之后才会继续执行线程2*/
new Thread(()->{phone1.sendMsg();},"A").start(); //下面两个Synchronized锁
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{phone2.sendEmail();},"B").start();
}
}
class Phone4{
//sychronized锁住的是方法的调用者,锁住的是对象,但是如果加上static之后锁住的就是类
// Class<Phone3> phone3Class = Phone3.class;
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("打电话!!!");
}
//普通方法当执行的时候不会被锁住
public synchronized void sendEmail(){
System.out.println("发邮箱!!!");
}
}
运行结果:
发邮箱!!!
发短信!!!
6、static修饰的部分sychronized锁(一个对象)
当static修饰一个类之后,如果一个方法只是加了一个普通的同步锁,那么不会受到static的影响。
package com.zha.thread.lock8;
import java.util.concurrent.TimeUnit;
/**
* @author zhazhalin
* @version 1.0
* @date 2021/3/14 15:28
*/
public class Lock5 {
public static void main(String[] args) {
Phone5 phone1=new Phone5();
new Thread(()->{phone1.sendMsg();},"A").start(); //下面两个Synchronized锁
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{phone1.call();},"B").start();
}
}
class Phone5{
//sychronized锁住的是方法的调用者,锁住的是对象,但是如果加上static之后锁住的就是类
// Class<Phone3> phone3Class = Phone3.class;
//static修饰的锁,锁住的是类模板
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("打电话!!!");
}
}
运行结果:
打电话!!!
发短信!!!