synchronized关键字的使用和理解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/HardToFindName/article/details/100741344

synchronized的作用和作用范围

在使用synchronized时,首先要清除其作用和作用的范围。
官方解释:

同步方法支持一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成的。

即被加锁的一方,在同一时刻只能被一个线程访问。

1. 对代码块加锁

private Object object = new Object();
synchronized (object) {
	System.out.println("lock");
}

使用synchronized对代码块加锁,需要一个凭证来证明当前代码块的锁已被一个线程持有,以拒绝其他线程在此刻访问,这个凭证为一个对象,可以理解为得到这个对象的许可才能进行下面的操作,但是每次只有许可一个线程,直到获得许可的线程将许可让出来,才能让下一个线程进来,如上所示便是object,也可以是对当前对象加锁synchronized (object)-->synchronized (this)

对方法的加锁

重点理解:
我们知道当我们编写一个新类的时候,jvm会帮我们编译成Class对象,存放在同名的.class文件中,在运行时,如果需要生成这个类的对象,jvm会检查此类有没有加载进内存,如果没有就会通过类加载器,将该.class文件装入到内存中,如是有,则根据此class文件生成对象。此在java类加载之后,方法体以字节码的形式存在于常量池,在对象执行方法的过程中,实际的对象和该方法没有什么关系,而是方法客观存在于一个地方了,只是在这个对象中含有怎么找到这个方法的信息。

2. 对非静态方法加锁

public class A implements Runnable{
	public void run(){
		test();
	}
	public synchronized void test() {
		System.out.println("test");
	}
}

此时加锁的对象是当前对像,即访问凭证为当前对象,相当于public synchronized(this) void test()

此时A的不同实例对象间不存在干扰,因为此时锁住的是当前对象寻找这个加锁方法的信息,如:

A a1 = new A();
A a2 = new A();
new Thread(a1,"t1").start();
new Thread(a2,"t2").start();

a1的test方法只是锁住a1,在一个线程获取到a1锁时,并不影响其他线程获取a2的锁。

3.对静态方法加锁

对静态方法加锁,加锁的对象是类的字节码对象,如:

public class A implements Runnable{
	public void run(){
		test();
	}
	
	public static synchronized void test() {
		System.out.println("test");
	}
}
A a1 = new A();
A a2 = new A();
new Thread(a1,"t1").start();
new Thread(a2,"t2").start();

由于上面的两个实例是根据同一个字节码对象生成的,如果对该字节码对象加锁,其锁住的方法其实是锁住了位于内存中那个方法体的字节码,所以,当一个线程持有一个对象该锁时,即使是其他线程拿到了不同的实例对象(同一个类),也无方访问这个方法,继而是影响所有的实例对象对该方法的访问。

总结

使用synchronized对代码进行同步,根据其不同的作用的范围,可以根据场景选取出不同同步形式,如商品秒杀场景,便不能使用synchronized修饰的方法便不能是静态的,想想秒杀的商品不止一个,也不太可能根据不同的商品编写不同的类,所以一旦这样进行编码,将会影响所有秒杀商品的秒杀效率。

个人见解,欢迎指正.

猜你喜欢

转载自blog.csdn.net/HardToFindName/article/details/100741344