java之synchronized关键字

synchronized关键字用法

看到网上很多讲synchronized关键字用法的文章,说的都很有道理,也很深刻,但是看完总感觉脑袋里还是有点乱乱的。经过一番自己的思考后,想从自己的思考角度出发,来说一说synchronized关键字的用法。在我看来,其实想将加锁后的访问规则讲清楚其实很简单。废话就不多说了,看下面的文字吧。

一、一套清晰的规则

其实很容易发现,无论是加了锁的方法还是加了锁的代码块,无是加的是对象锁还是加的是类锁,只要上的是同一把锁,那么它们的访问规则就应该是相同的。也就是上了同一把锁的东西,要么一起可以被访问,要么一起禁止别人访问。因此,如果想搞清楚访问的规则,我们首先要搞清楚锁的类型。然后判断,只要上的是同一把锁,访问的规则就应该相同。那么java中的锁有那些类型呢。可以简单的总结为两种类型:

java之synchronized关键字

应该注意的是,因为一个类只有一个.class对象,因此所有的访问者在访问被加了类锁的东西时,都是共用同一把锁,而类的实例却可以有很多个,因此不同对象访问加了对象锁的东西,它们的访问互不干扰。

知道了锁的类型,那么我们就可以总结出一个通用且清晰的规则了。如下:

java之synchronized关键字

然后再来看怎么判断什么情况下是相同的锁。如下:

java之synchronized关键字

好了,也就是说我们判断访问的规则,就是基于个步骤:

1.首先判断是不是同一把锁

2.然后判断各自的访问规则

下面我们来看几个实际例子,来看看我们这个规则到底怎么使用。

二、使用这个规则

(1)实验一

新建一个java项目,新建一个SynTest类,在其中写如下代码:

java之synchronized关键字

java之synchronized关键字

在代码中,可以看到我们给test1方法上了对象锁,加锁的方式是给方法加锁,加的是当前对象锁;而给test2方法上也上了对象锁,加锁的方式给代码块加锁,注意上的是当前对象锁,你也可以将this替换成任意其他对象。然后我们在main方法中看到,新建了两个线程,都是用同一个对象来调用这两个方法。因此,可以判定线程t1和t2使用的是同一把锁,因此访问规则就是t1获得了这把锁后,tes1方法和test2中被加锁的代码块都允许t1访问,都拒绝t2访问,等t1运行完,t2才会获得该锁,进行访问。因此输出的结果很明显了,t1执行完,再执行t2。我们运行下程序,看看我们按照规则来推理的是否正确,运行结果如下:

java之synchronized关键字

和我们预想的完全一样!!

然后我们再做个试验,比如将main方法中的代码改成下面的:

java之synchronized关键字

只是多出一个实例而已,然后线程t2通过st2来调用test2。那么就test1和test2加的都是当前对象的锁,显然它们的当前对象不同吧。所以它们不是同一把锁,互相不干扰。那么我们运行程序,效果如下:

java之synchronized关键字

果然它们各自运行各自的,所以没什么顺序。

怎么样,这样子来判断访问权限是不是很简单啊。

(2)实验二

然后修改SynTest的代码,如下:

java之synchronized关键字

java之synchronized关键字

很简单,test1是一个静态方法,所以它加的锁是类锁,而test2加了一个类锁,是加载了一个代码块中,因此他们加的是相同的锁。运行程序,结果如下:

java之synchronized关键字

即t1访问完,t2再访问。

然后我们再修改SynTest中的代码,如下:

java之synchronized关键字

java之synchronized关键字

代码中很显然了,test1是静态方法,它上的是类锁。而test2是普通方法,它上的对象锁。这是不同的锁。所以t1访问test1时,test2方法是不受干扰的,t2肯定可以同时访问test2.因此打印顺序为任意顺序。如下:

java之synchronized关键字

为乱序。此时如果你的打印结果为先打印出t1的结果再是t2的结果,也不必惊讶,因为程序简单,循环次数少,CPU性能高,所以很可能t2缸启动就瞬间运行完了t1。

(3)实验三

我们再修改SynTest的代码,如下:

java之synchronized关键字

java之synchronized关键字

很明显一个类锁,一个对象锁,访问规则互不干扰。即,t1访问test1方法时,并不影响t2访问test2(但是会禁止t2访问test1,因为t2用的也是st对象,此时t1已给st对象上锁了)。所以打印顺序可能为任意顺序。好了,运行程序,结果如下:

java之synchronized关键字

注意:此时如果你的打印结果为先打印出t1的结果再是t2的结果,也不必惊讶,因为程序简单,循环次数少,CPU性能高,所以很可能t2缸启动就瞬间运行完了t1。

好了,还有很多的实验我们就不再做了,都是根据上面总结的规则来做的。相信这下,你对上锁和上锁后的访问规则都清楚了吧。

三、如果你还不理解的话

如果你还不理解的话,这是某位大牛做的一个很好的比喻。你可以看看。摘抄如下:

java之synchronized关键字

java之synchronized关键字

java之synchronized关键字