C来,很重要?/p>Java语言的关键字Q当它用来修C个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线E执行该D代码?
一、当两个q发U程讉K同一个对象object中的q个synchronized(this)同步代码块时Q一个时间内只能有一个线E得到执行。另一个线E必ȝ待当前线E执行完q个代码块以后才能执行该代码块?/p>
二、然而,当一个线E访问object的一个synchronized(this)同步代码块时Q另一个线E仍然可以访问该object中的非synchronized(this)同步代码块?/p>
三、尤其关键的是,当一个线E访问object的一个synchronized(this)同步代码块时Q其他线E对object中所有其它synchronized(this)同步代码块的讉K被d?/p>
四、第三个例子同样适用其它同步代码块。也是_当一个线E访问object的一个synchronized(this)同步代码块时Q它p得了q个object的对象锁。结果,其它U程对该object对象所有同步代码部分的讉K都被暂时d?/p>
五、以上规则对其它对象锁同样适用.
举例说明Q?nbsp;
一、当两个q发U程讉K同一个对象object中的q个synchronized(this)同步代码块时Q一个时间内只能有一个线E得到执行。另一个线E必ȝ待当前线E执行完q个代码块以后才能执行该代码块?/p>
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();
}
}
l果Q?nbsp;
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
二、然而,当一个线E访问object的一个synchronized(this)同步代码块时Q另一个线E仍然可以访问该object中的非synchronized(this)同步代码块?/p>
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();
}
}
l果Q?nbsp;
t1 : 4
t2 : 4
t1 : 3
t2 : 3
t1 : 2
t2 : 2
t1 : 1
t2 : 1
t1 : 0
t2 : 0
三、尤其关键的是,当一个线E访问object的一个synchronized(this)同步代码块时Q其他线E对object中所有其它synchronized(this)同步代码块的讉K被d?/p>
//修改Thread2.m4t2()ҎQ?nbsp;
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) {
}
}
}
}
l果Q?/p>
t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0
四、第三个例子同样适用其它同步代码块。也是_当一个线E访问object的一个synchronized(this)同步代码块时Q它p得了q个object的对象锁。结果,其它U程对该object对象所有同步代码部分的讉K都被暂时d?/p>
//修改Thread2.m4t2()Ҏ如下Q?/p>
public synchronized void m4t2() {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
l果Q?nbsp;
t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0
五、以上规则对其它对象锁同样适用:
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) { //使用对象?nbsp;
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();
}
}
l果Q?/p>
管U程t1获得了对Inner的对象锁Q但׃U程t2讉K的是同一个Inner中的非同步部分。所以两个线E互不干扰?/p>
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()前面加上synchronizedQ?/p>
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) {
}
}
}
l果Q?/p>
管U程t1与t2讉K了同一个Inner对象中两个毫不相关的部分,但因为t1先获得了对Inner的对象锁Q所以t2对Inner.m4t2()的访问也被阻塞,因ؓm4t2()是Inner中的一个同步方法?/p>
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
W二:
synchronized 关键字,它包括两U用法:synchronized Ҏ?synchronized 块?nbsp;
1. synchronized ҎQ通过在方法声明中加入 synchronized关键字来声明 synchronized Ҏ。如Q?nbsp;
public synchronized void accessVal(int newVal);
synchronized Ҏ控制对类成员变量的访问:每个cd例对应一把锁Q每?synchronized Ҏ都必获得调用该Ҏ的类实例的锁方能
执行Q否则所属线E阻塞,Ҏ一旦执行,q占该锁,直到从该Ҏq回时才锁释放Q此后被d的线E方能获得该锁,重新q入可执?/p>
状态。这U机制确保了同一时刻对于每一个类实例Q其所有声明ؓ synchronized 的成员函C臛_只有一个处于可执行状态(因ؓ臛_只有
一个能够获得该cd例对应的锁)Q从而有效避免了cL员变量的讉K冲突Q只要所有可能访问类成员变量的方法均被声明ؓ synchronizedQ?/p>
?nbsp;
?Java 中,不光是类实例Q每一个类也对应一把锁Q这h们也可将cȝ静态成员函数声明ؓ synchronized Q以控制其对cȝ静态成
员变量的讉K?nbsp;
synchronized Ҏ的缺P若将一个大的方法声明ؓsynchronized 会大大影响效率Q典型地Q若线E类的方?run() 声明?/p>
synchronized Q由于在U程的整个生命期内它一直在q行Q因此将D它对本类M synchronized Ҏ的调用都永远不会成功。当然我们可
以通过访问类成员变量的代码放C门的Ҏ中,其声明?synchronized Qƈ在主Ҏ中调用来解决q一问题Q但?Java 为我们提?/p>
了更好的解决办法Q那是 synchronized 块?nbsp;
2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:
synchronized(syncObject) {
//允许讉K控制的代?nbsp;
}
synchronized 块是q样一个代码块Q其中的代码必须获得对象 syncObject Q如前所qͼ可以是类实例或类Q的锁方能执行,具体?/p>
制同前所q。由于可以针对Q意代码块Q且可Q意指定上锁的对象Q故灉|性较高?nbsp;
对synchronized(this)的一些理?nbsp;
一、当两个q发U程讉K同一个对象object中的q个synchronized(this)同步代码块时Q一个时间内只能有一个线E得到执行。另一个线
E必ȝ待当前线E执行完q个代码块以后才能执行该代码块?nbsp;
二、然而,当一个线E访问object的一个synchronized(this)同步代码块时Q另一个线E仍然可以访问该object中的非synchronized
(this)同步代码块?nbsp;
三、尤其关键的是,当一个线E访问object的一个synchronized(this)同步代码块时Q其他线E对object中所有其它synchronized(this)
同步代码块的讉K被d?nbsp;
四、第三个例子同样适用其它同步代码块。也是_当一个线E访问object的一个synchronized(this)同步代码块时Q它p得了q个
object的对象锁。结果,其它U程对该object对象所有同步代码部分的讉K都被暂时d?nbsp;
五、以上规则对其它对象锁同样适用
http://hi.baidu.com/sunshibing/blog/item/5235b9b731d48ff430add14a.html
java中synchronized用法
打个比方Q一个object像一个大房子Q大门永q打开。房子里?很多戉KQ也是ҎQ?/p>
q些戉K有上锁的QsynchronizedҎQ, 和不上锁之分Q普通方法)。房门口攄一把钥匙(keyQ,q把钥匙可以打开所有上锁的戉K?/p>
另外我把所有想调用该对象方法的U程比喻成想q入q房子某?戉K的h。所有的东西p么多了,下面我们看看q些东西之间如何作用的?/p>
在此我们先来明确一下我们的前提条g。该对象臛_有一个synchronizedҎQ否则这个keyq有啥意义。当然也׃会有我们的这个主题了?/p>
一个h惌入某间上了锁的房_他来到房子门口,看见钥匙在那儿(说明暂时q没有其他h要用上锁的 戉KQ。于是他CLC钥匙
Qƈ且按照自?的计划用那些房间。注意一点,他每ơ用完一ơ上锁的戉K后会马上把钥匙还回去。即使他要连l用两间上锁的戉KQ?/p>
中间他也要把钥匙q回去,再取回来?/p>
因此Q普通情况下钥匙的用原则是Q?#8220;随用随借,用完卌?#8221;
q时其他人可以不受限制的使用那些不上锁的戉KQ一个h用一间可以,两个人用一间也可以Q没限制。但是如果当某个人想要进入上锁的?/p>
_他就要跑到大门口ȝ看了。有钥匙当然拿了pQ没有的话,只能等了?/p>
要是很多人在{这把钥匙,{钥匙还回来以后Q谁会优先得到钥匙?Not guaranteed。象前面例子里那个想q箋使用两个上锁戉K的家伙,?/p>
中间q钥匙的时候如果还有其他h在等钥匙Q那么没有Q何保证这家伙能再ơ拿到?QJAVA规范在很多地斚w明确说明不保证,?/p>
Thread.sleep()休息后多久会q回q行Q相同优先权的线E那个首先被执行Q当要访问对象的锁被 释放后处于等待池的多个线E哪个会优先?/p>
刎ͼ{等。我xl的军_权是在JVMQ之所以不保证Q就是因为JVM在做Zq决定的时候,l不是简单单Ҏ 一个条件来做出判断Q而是
Ҏ很多条。而由于判断条件太多,如果说出来可能会影响JAVA的推q,也可能是因ؓ知识产权保护的原因吧。SUNl了个不保证 q去?/p>
。无可厚非。但我相信这些不定Qƈ非完全不定。因机q东西本w就是按指oq行的。即使看h很随机的现象Q其实都是有规律
可寻。学q?计算机的都知道,计算机里随机数的学名是伪随机敎ͼ是hq用一定的Ҏ写出来的Q看上去随机|了。另外,或许是因惛_
的确定太费事Q也没多大意义,所 以不定׃定了吧。)
再来看看同步代码块。和同步Ҏ有小的不同?/p>
1.从尺怸Ԍ同步代码块比同步Ҏ。你可以把同步代码块看成是没上锁戉K里的一块用带锁的屏风隔开的空间?/p>
2.同步代码块还可以Zؓ的指定获得某个其它对象的key。就像是指定用哪一把钥匙才能开q个屏风的锁Q你可以用本房的钥匙Q你也可以指?/p>
用另一个房子的钥匙才能开Q这L话,你要跑到另一栋房子那儿把那个钥匙拿来Qƈ用那个房子的钥匙来打开q个房子的带锁的屏风?/p>
C你获得的那另一栋房子的钥匙Qƈ不媄响其他hq入那栋房子没有锁的戉K?/p>
Z么要使用同步代码块呢Q我惛_该是q样的:首先对程序来讲同步的部分很媄响运行效率,而一个方法通常是先创徏一些局部变
量,再对q些变量做一?操作Q如q算Q显C等{;而同步所覆盖的代码越多,Ҏ率的影响p严重。因此我们通常量~小其媄响范围?/p>
如何做?同步代码块。我们只把一个方法中该同 步的地方同步Q比如运?/p>
另外Q同步代码块可以指定钥匙q一特点有个额外的好处,是可以在一定时期内霸占某个对象的key。还记得前面说过普通情况下?/p>
匙的使用原则吗。现在不是普通情况了。你所取得的那把钥匙不是永q不q,而是在退出同步代码块时才q?/p>
q用前面那个惌l用两个上锁戉K的家伙打比方。怎样才能在用完一间以后,l箋使用另一间呢。用同步代码块吧。先创徏另外
一个线E,做一个同步代?块,把那个代码块的锁指向q个房子的钥匙。然后启动那个线E。只要你能在q入那个代码块时抓到q房子的钥匙
Q你可以一直保留到退出那个代码块。也是?你甚臛_以对本房内所有上锁的戉K遍历Q甚臛_sleep(10*60*1000)Q而房门口却还?/p>
1000个线E在{这把钥匙呢。很q瘾吧?/p>
在此对sleep()Ҏ和钥匙的兌性讲一下。一个线E在拿到key后,且没有完成同步的内容Ӟ如果被强制sleep()了,那keyq一
直在 它那ѝ直到它再次q行Q做完所有同步内容,才会归还key。记住,那家伙只是干zd累了Q去休息一下,他ƈ没干完他要干的事。ؓ
了避免别入那个房?把里面搞的一团糟Q即使在睡觉的时候他也要把那唯一的钥匙戴在n上?/p>
最后,也许有h会问Qؓ什么要一把钥匙通开Q而不是一个钥匙一个门呢?我想q纯_Ҏ因ؓ复杂性问题。一个钥匙一个门当然?/p>
安全Q但是会牉|好多问题。钥?的生,保管Q获得,归还{等。其复杂性有可能随同步方法的增加呈几何数增加,严重影响效率。这?/p>
是一个权衡的问题吧。ؓ了增加一点点安全性,D?率大大降低,是多么不可取啊?/p>
synchronized的一个简单例?/p>
public class TextThread {
public static void main(String[] args) {
TxtThread tt = new TxtThread();
new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start();
}
}
class TxtThread implements Runnable {
int num = 100;
String str = new String();
public void run() {
synchronized (str) {
while (num > 0) {
try {
Thread.sleep(1);
} catch (Exception e) {
e.getMessage();
}
System.out.println(Thread.currentThread().getName()
+ "this is " + num--);
}
}
}
}
上面的例子中Z刉一个时间差,也就是出错的Z,使用了Thread.sleep(10)
Java对多U程的支持与同步机制深受大家的喜爱,g看v来用了synchronized关键字就可以L地解军_U程׃n数据同步问题。到底如
何?――q得对synchronized关键字的作用q行深入了解才可定论?/p>
ȝ说来Qsynchronized关键字可以作为函数的修饰W,也可作ؓ函数内的语句Q也是qx说的同步Ҏ和同步语句块。如果再l的分类Q?/p>
synchronized可作用于instance变量、object referenceQ对象引用)、static函数和class literals(cdU字面常?w上?/p>
在进一步阐qC前,我们需要明几点:
AQ无论synchronized关键字加在方法上q是对象上,它取得的锁都是对象,而不是把一D代码或函数当作?#8213;―而且同步Ҏ很可能还会被?/p>
他线E的对象讉K?/p>
BQ每个对象只有一个锁QlockQ与之相兌?/p>
CQ实现同步是要很大的pȝ开销作ؓ代h的,甚至可能造成死锁Q所以尽量避免无谓的同步控制?/p>
接着来讨论synchronized用到不同地方对代码生的影响Q?/p>
假设P1、P2是同一个类的不同对象,q个cM定义了以下几U情늚同步块或同步ҎQP1、P2都可以调用它们?/p>
1Q?把synchronized当作函数修饰W时Q示例代码如下:
Public synchronized void methodAAA()
{
//….
}
q也是同步ҎQ那q时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也是_当一个对象P1在不同的U程?/p>
执行q个同步ҎӞ它们之间会Ş成互斥,辑ֈ同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以Q意调用这个被加了
synchronized关键字的Ҏ?/p>
上边的示例代码等同于如下代码Q?/p>
public void methodAAA()
{
synchronized (this) // (1)
{
//…..
}
}
(1)处的this指的是什么呢Q它指的是调用q个Ҏ的对象,如P1。可见同步方法实质是synchronized作用于object reference?#8213;―那个
拿到了P1对象锁的U程Q才可以调用P1的同步方法,而对P2而言QP1q个锁与它毫不相qԌE序也可能在q种情Ş下摆脱同步机制的控制Q?/p>
成数据乱:Q?/p>
2Q同步块Q示例代码如下:
public void method3(SomeObject so)
{
synchronized(so)
{
//…..
}
}
q时Q锁是soq个对象Q谁拿到q个锁谁可以运行它所控制的那D代码。当有一个明的对象作ؓ锁时Q就可以q样写程序,但当没有?/p>
的对象作ؓ锁,只是惌一D代码同步时Q可以创Z个特D的instance变量Q它得是一个对象)来充当锁Q?/p>
class Foo implements Runnable
{
private byte[] lock = new byte[0]; // Ҏ的instance变量
Public void methodA()
{
synchronized(lock) { //… }
}
//…..
}
注:雉度的byte数组对象创徏h比M对象都经?#8213;―查看~译后的字节码:生成雉度的byte[]对象只需3条操作码Q而Object lock
= new Object()则需?行操作码?/p>
3Q将synchronized作用于static 函数Q示例代码如下:
Class Foo
{
public synchronized static void methodAAA() // 同步的static 函数
{
//….
}
public void methodBBB()
{
synchronized(Foo.class) // class literal(cdU字面常?
}
}
代码中的methodBBB()Ҏ是把class literal作ؓ锁的情况Q它和同步的static函数产生的效果是一LQ取得的锁很特别Q是当前调用q?/p>
个方法的对象所属的c(ClassQ而不再是p个Class产生的某个具体对象了Q?/p>
记得在《Effective Java》一书中看到q将 Foo.class?P1.getClass()用于作同步锁q不一P不能用P1.getClass()来达到锁q个Class?/p>
目的。P1指的是由FoocM生的对象?/p>
可以推断Q如果一个类中定义了一个synchronized的static函数AQ也定义了一个synchronized 的instance函数BQ那么这个类的同一对象Obj
在多U程中分别访问A和B两个ҎӞ不会构成同步Q因为它们的锁都不一栗AҎ的锁是Objq个对象Q而B的锁是Obj所属的那个Class?/p>
结如下Q?/p>
搞清楚synchronized锁定的是哪个对象Q就能帮助我们设计更安全的多U程E序?/p>
q有一些技巧可以让我们对共享资源的同步讉K更加安全Q?/p>
1Q?定义private 的instance变量+它的 getҎQ而不要定义public/protected的instance变量。如果将变量定义为publicQ对象在外界可以
l过同步Ҏ的控制而直接取得它Qƈ改动它。这也是JavaBean的标准实现方式之一?/p>
2Q?如果instance变量是一个对象,如数l或ArrayList什么的Q那上述Ҏ仍然不安全,因ؓ当外界对象通过getҎ拿到q个instance对象
的引用后Q又其指向另一个对象,那么q个private变量也就变了Q岂不是很危险?q个时候就需要将getҎ也加上synchronized同步Qƈ
且,只返回这个private对象的clone()――q样Q调用端得到的就是对象副本的引用?/p>
]]>