同步方法和同步代码块

打个比方:一个object就像一个大房子,大门永远打开。房子里有不少房间(也就是方法)。这些房间有上锁的(synchronized方法),和不上锁之分(普通方法)。房门口放着一把钥匙(key),这把钥匙能够打开全部上锁的房间。另外我把全部想调用该对象方法的线程比喻成想进入这房子某个房间的人。全部的东西就这么多了,下面咱们看看这些东西之间如何做用的。安全

在此咱们先来明确一下咱们的前提条件。该对象至少有一个synchronized方法,不然这个key还有啥意义。固然也就不会有咱们的这个主题了。并发

一我的想进入某间上了锁的房间,他来到房子门口,看见钥匙在那儿(说明暂时尚未其余人要使用上锁的房间)。因而他走上去拿到了钥匙,而且按照本身的计划使用那些房间。注意一点,他每次使用完一次上锁的房间后会立刻把钥匙还回去。即便他要连续使用两间上锁的房间,中间他也要把钥匙还回去,再取回来。oop

所以,普通状况下钥匙的使用原则是:“随用随借,用完即还。”this

这时其余人能够不受限制的使用那些不上锁的房间,一我的用一间能够,两我的用一间也能够,没限制。可是若是当某我的想要进入上锁的房间,他就要跑到大门口去看看了。有钥匙固然拿了就走,没有的话,就只能等了。线程

要是不少人在等这把钥匙,等钥匙还回来之后,谁会优先获得钥匙?Not guaranteed。象前面例子里那个想连续使用两个上锁房间的家伙,他中间还钥匙的时候若是还有其余人在等钥匙,那么没有任何保证这家伙能再次拿到。(JAVA规范在不少地方都明确说明不保证,象Thread.sleep()休息后多久会返回运行,相同优先权的线程那个首先被执行,当要访问对象的锁被释放后处于等待池的多个线程哪一个会优先获得,等等。我想最终的决定权是在JVM,之因此不保证,就是由于JVM在作出上述决定的时候,毫不是简简单单根据一个条件来作出判断,而是根据不少条。而因为判断条件太多,若是说出来可能会影响JAVA的推广,也多是由于知识产权保护的缘由吧。SUN给了个不保证就混过去了。无可厚非。但我相信这些不肯定,并不是彻底不肯定。由于计算机这东西自己就是按指令运行的。即便看起来很随机的现象,其实都是有规律可寻。学过计算机的都知道,计算机里随机数的学名是伪随机数,是人运用必定的方法写出来的,看上去随机罢了。另外,或许是由于要想弄的肯定太费事,也没多大意义,因此不肯定就不肯定了吧。)对象

再来看看同步代码块。和同步方法有小小的不一样。get

1.从尺寸上讲,同步代码块比同步方法小。你能够把同步代码块当作是没上锁房间里的一块用带锁的屏风隔开的空间。同步

2.同步代码块还能够人为的指定得到某个其它对象的key。就像是指定用哪一把钥匙才能开这个屏风的锁,你能够用本房的钥匙;你也能够指定用另外一个房子的钥匙才能开,这样的话,你要跑到另外一栋房子那儿把那个钥匙拿来,并用那个房子的钥匙来打开这个房子的带锁的屏风。io

记住你得到的那另外一栋房子的钥匙,并不影响其余人进入那栋房子没有锁的房间。class

为何要使用同步代码块呢?我想应该是这样的:首先对程序来说同步的部分很影响运行效率,而一个方法一般是先建立一些局部变量,再对这些变量作一些操做,如运算,显示等等;而同步所覆盖的代码越多,对效率的影响就越严重。所以咱们一般尽可能缩小其影响范围。如何作?同步代码块。咱们只把一个方法中该同步的地方同步,好比运算。

另外,同步代码块能够指定钥匙这一特色有个额外的好处,是能够在必定时期内霸占某个对象的key。还记得前面说过普通状况下钥匙的使用原则吗。如今不是普通状况了。你所取得的那把钥匙不是永远不还,而是在退出同步代码块时才还。

还用前面那个想连续用两个上锁房间的家伙打比方。怎样才能在用完一间之后,继续使用另外一间呢。用同步代码块吧。先建立另一个线程,作一个同步代码块,把那个代码块的锁指向这个房子的钥匙。而后启动那个线程。只要你能在进入那个代码块时抓到这房子的钥匙,你就能够一直保留到退出那个代码块。也就是说你甚至能够对本房内全部上锁的房间遍历,甚至再sleep(10601000),而房门口却还有1000个线程在等这把钥匙呢。很过瘾吧。

在此对sleep()方法和钥匙的关联性讲一下。一个线程在拿到key后,且没有完成同步的内容时,若是被强制sleep()了,那key还一直在它那儿。直到它再次运行,作完全部同步内容,才会归还key。记住,那家伙只是干活干累了,去休息一下,他并没干完他要干的事。为了不别人进入那个房间把里面搞的一团糟,即便在睡觉的时候他也要把那惟一的钥匙戴在身上。

最后,也许有人会问,为何要一把钥匙通开,而不是一个钥匙一个门呢?我想这纯粹是由于复杂性问题。一个钥匙一个门固然更安全,可是会牵扯好多问题。钥匙的产生,保管,得到,归还等等。其复杂性有可能随同步方法的增长呈几何级数增长,严重影响效率。

这也算是一个权衡的问题吧。为了增长一点点安全性,致使效率大大下降,是多么不可取啊。

摘自:http://www.54bk.com/more.asp?name=czp&id=2097

1、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程获得执行。另外一个线程必须等待当前线程执行完这个代码块之后才能执行该代码块。

2、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另外一个线程仍然能够访问该object中的非synchronized(this)同步代码块。

3、尤为关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其余线程对object中全部其它synchronized(this)同步代码块的访问将被阻塞。

4、第三个例子一样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就得到了这个object的对象锁。结果,其它线程对该object对象全部同步代码部分的访问都被暂时阻塞。

5、以上规则对其它对象锁一样适用.

举例说明:

1、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程获得执行。另外一个线程必须等待当前线程执行完这个代码块之后才能执行该代码块。

package ths;

public class Thread1 implements Runnable { public void run() { synchronized(this) { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " synchronized loop " + i); } } } public static void main(String[] args) { Thread1 t1 = new Thread1(); Thread ta = new Thread(t1, "A"); Thread tb = new Thread(t1, "B"); ta.start(); tb.start(); } }

结果:

A synchronized loop 0 A synchronized loop 1 A synchronized loop 2 A synchronized loop 3 A synchronized loop 4 B synchronized loop 0 B synchronized loop 1 B synchronized loop 2 B synchronized loop 3 B synchronized loop 4

2、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另外一个线程仍然能够访问该object中的非synchronized(this)同步代码块。

package ths;

public class Thread2 { public void m4t1() { synchronized(this) { int i = 5; while( i-- > 0) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } } } public void m4t2() { int i = 5; while( i-- > 0) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } } public static void main(String[] args) { final Thread2 myt2 = new Thread2(); Thread t1 = new Thread( new Runnable() { public void run() { myt2.m4t1(); } }, "t1" ); Thread t2 = new Thread( new Runnable() { public void run() { myt2.m4t2(); } }, "t2" ); t1.start(); t2.start(); } }

结果:

t1 : 4 t2 : 4 t1 : 3 t2 : 3 t1 : 2 t2 : 2 t1 : 1 t2 : 1 t1 : 0 t2 : 0

3、尤为关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其余线程对object中全部其它synchronized(this)同步代码块的访问将被阻塞。

//修改Thread2.m4t2()方法:

public void m4t2() { synchronized(this) { int i = 5; while( i-- > 0) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } }

}

结果:

t1 : 4 t1 : 3 t1 : 2 t1 : 1 t1 : 0 t2 : 4 t2 : 3 t2 : 2 t2 : 1 t2 : 0

4、第三个例子一样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就得到了这个object的对象锁。结果,其它线程对该object对象全部同步代码部分的访问都被暂时阻塞。

//修改Thread2.m4t2()方法以下:

public synchronized void m4t2() { int i = 5; while( i-- > 0) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } }

结果:

t1 : 4 t1 : 3 t1 : 2 t1 : 1 t1 : 0 t2 : 4 t2 : 3 t2 : 2 t2 : 1 t2 : 0

5、以上规则对其它对象锁一样适用:

package ths;

public class Thread3 { class Inner { private void m4t1() { int i = 5; while(i-- > 0) { System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i); try { Thread.sleep(500); } catch(InterruptedException ie) { } } } private void m4t2() { int i = 5; while(i-- > 0) { System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i); try { Thread.sleep(500); } catch(InterruptedException ie) { } } } } private void m4t1(Inner inner) { synchronized(inner) { //使用对象锁 inner.m4t1(); } } private void m4t2(Inner inner) { inner.m4t2(); } public static void main(String[] args) { final Thread3 myt3 = new Thread3(); final Inner inner = myt3.new Inner(); Thread t1 = new Thread( new Runnable() { public void run() { myt3.m4t1(inner); } }, "t1" ); Thread t2 = new Thread( new Runnable() { public void run() { myt3.m4t2(inner); } }, "t2" ); t1.start(); t2.start(); } }

结果:

尽管线程t1得到了对Inner的对象锁,但因为线程t2访问的是同一个Inner中的非同步部分。因此两个线程互不干扰。

t1 : Inner.m4t1()=4 t2 : Inner.m4t2()=4 t1 : Inner.m4t1()=3 t2 : Inner.m4t2()=3 t1 : Inner.m4t1()=2 t2 : Inner.m4t2()=2 t1 : Inner.m4t1()=1 t2 : Inner.m4t2()=1 t1 : Inner.m4t1()=0 t2 : Inner.m4t2()=0

如今在Inner.m4t2()前面加上synchronized:

private synchronized void m4t2() { int i = 5; while(i-- > 0) { System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i); try { Thread.sleep(500); } catch(InterruptedException ie) { } } }

结果:

尽管线程t1与t2访问了同一个Inner对象中两个绝不相关的部分,但由于t1先得到了对Inner的对象锁,因此t2对Inner.m4t2()的访问也被阻塞,由于m4t2()是Inner中的一个同步方法。

t1 : Inner.m4t1()=4 t1 : Inner.m4t1()=3 t1 : Inner.m4t1()=2 t1 : Inner.m4t1()=1 t1 : Inner.m4t1()=0 t2 : Inner.m4t2()=4 t2 : Inner.m4t2()=3 t2 : Inner.m4t2()=2 t2 : Inner.m4t2()=1 t2 : Inner.m4t2()=0

相关文章
相关标签/搜索