Java语言的keyword。当它用来修饰一个方法或者一个代码块的时候,可以保证在同一时刻最多仅仅有一个线程运行该段代码。
一、当两个并发线程訪问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内仅仅能有一个线程得到运行。还有一个线程必须等待当前线程运行完这个代码块以后才干运行该代码块。
二、然而。当一个线程訪问object的一个synchronized(this)同步代码块时。还有一个线程仍然能够訪问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程訪问object的一个synchronized(this)同步代码块时。其他线程对object中全部其他synchronized(this)同步代码块的訪问将被堵塞。
四、第三个样例相同适用其他同步代码块。
也就是说,当一个线程訪问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其他线程对该object对象全部同步代码部分的訪问都被临时堵塞。
五、以上规则对其他对象锁相同适用.
举例说明:
一、当两个并发线程訪问同一个对象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
二、然而。当一个线程訪问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
三、尤其关键的是,当一个线程訪问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
四、第三个样例相同适用其他同步代码块。也就是说。当一个线程訪问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
五、以上规则对其他对象锁相同适用:
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
第二篇:
synchronized keyword,它包含两种使用方法:synchronized 方法和 synchronized 块。
1. synchronized 方法:通过在方法声明中增加 synchronizedkeyword来声明 synchronized 方法。
如:
public synchronized void accessVal(int newVal);
synchronized 方法控制对类成员变量的訪问:每一个类实例相应一把锁,每一个 synchronized 方法都必须获得调用该方法的类实例的锁方能
运行,否则所属线程堵塞,方法一旦运行,就独占该锁,直到从该方法返回时才将锁释放,此后被堵塞的线程方能获得该锁,又一次进入可运行
状态。这样的机制确保了同一时刻对于每个类实例。其全部声明为 synchronized 的成员函数中至多仅仅有一个处于可运行状态(由于至多仅仅有
一个可以获得该类实例相应的锁)。从而有效避免了类成员变量的訪问冲突(仅仅要全部可能訪问类成员变量的方法均被声明为 synchronized)
。
在 Java 中,不光是类实例,每个类也相应一把锁。这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成
员变量的訪问。
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为
synchronized ,因为在线程的整个生命期内它一直在执行,因此将导致它对本类不论什么 synchronized 方法的调用都永远不会成功。当然我们可
以通过将訪问类成员变量的代码放到专门的方法中。将其声明为 synchronized ,并在主方法中调用来解决这一问题,可是 Java 为我们提供
了更好的解决的方法,那就是 synchronized 块。
2. synchronized 块:通过 synchronizedkeyword来声明synchronized 块。语法例如以下:
synchronized(syncObject) {
//同意訪问控制的代码
}
synchronized 块是这样一个代码块。当中的代码必须获得对象 syncObject (如前所述,能够是类实例或类)的锁方能运行,详细机
制同前所述。因为能够针对随意代码块。且可随意指定上锁的对象,故灵活性较高。
对synchronized(this)的一些理解
一、当两个并发线程訪问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内仅仅能有一个线程得到运行。
还有一个线
程必须等待当前线程运行完这个代码块以后才干运行该代码块。
二、然而。当一个线程訪问object的一个synchronized(this)同步代码块时。还有一个线程仍然能够訪问该object中的非synchronized
(this)同步代码块。
三、尤其关键的是。当一个线程訪问object的一个synchronized(this)同步代码块时。其他线程对object中全部其他synchronized(this)
同步代码块的訪问将被堵塞。
四、第三个样例相同适用其他同步代码块。
也就是说。当一个线程訪问object的一个synchronized(this)同步代码块时,它就获得了这个
object的对象锁。结果,其他线程对该object对象全部同步代码部分的訪问都被临时堵塞。
五、以上规则对其他对象锁相同适用
http://hi.baidu.com/sunshibing/blog/item/5235b9b731d48ff430add14a.html
java中synchronized使用方法
打个例如:一个object就像一个大房子,大门永远打开。
房子里有 非常多房间(也就是方法)。
这些房间有上锁的(synchronized方法), 和不上锁之分(普通方法)。
房门口放着一把钥匙(key)。这把钥匙能够打开全部上锁的房间。
另外我把全部想调用该对象方法的线程比喻成想进入这房子某个 房间的人。全部的东西就这么多了,以下我们看看这些东西之间怎样作用的。
在此我们先来明白一下我们的前提条件。该对象至少有一个synchronized方法,否则这个key还有啥意义。
当然也就不会有我们的这个主题了。
一个人想进入某间上了锁的房间,他来到房子门口,看见钥匙在那儿(说明临时还没有其它人要使用上锁的 房间)。于是他走上去拿到了钥匙
。而且依照自己 的计划使用那些房间。注意一点,他每次使用完一次上锁的房间后会立即把钥匙还回去。即使他要连续使用两间上锁的房间,
中间他也要把钥匙还回去,再取回来。
因此,一般情况下钥匙的使用原则是:“随用随借,用完即还。
”
这时其它人能够不受限制的使用那些不上锁的房间。一个人用一间能够。两个人用一间也能够。没限制。
可是假设当某个人想要进入上锁的房
间,他就要跑到大门口去看看了。有钥匙当然拿了就走,没有的话,就仅仅能等了。
要是非常多人在等这把钥匙,等钥匙还回来以后。谁会优先得到钥匙?Not guaranteed。象前面样例里那个想连续使用两个上锁房间的家伙。他
中间还钥匙的时候假设还有其它人在等钥匙。那么没有不论什么保证这家伙能再次拿到。 (JAVA规范在非常多地方都明白说明不保证,象
Thread.sleep()歇息后多久会返回执行,同样优先权的线程那个首先被执行,当要訪问对象的锁被 释放后处于等待池的多个线程哪个会优先得
到,等等。我想终于的决定权是在JVM,之所以不保证,就是由于JVM在做出上述决定的时候,绝不是简简单单依据 一个条件来做出推断,而是
依据非常多条。而由于推断条件太多。假设说出来可能会影响JAVA的推广,也可能是由于知识产权保护的原因吧。SUN给了个不保证 就混过去了
。无可厚非。但我相信这些不确定。并不是全然不确定。由于计算机这东西本身就是按指令执行的。
即使看起来非常随机的现象,事实上都是有规律
可寻。学过 计算机的都知道,计算机里随机数的学名是伪随机数,是人运用一定的方法写出来的,看上去随机罢了。另外。也许是由于要想弄
的确定太费事。也没多大意义,所 以不确定就不确定了吧。
)
再来看看同步代码块。
和同步方法有小小的不同。
1.从尺寸上讲,同步代码块比同步方法小。你能够把同步代码块看成是没上锁房间里的一块用带锁的屏风隔开的空间。
2.同步代码块还能够人为的指定获得某个其他对象的key。就像是指定用哪一把钥匙才干开这个屏风的锁。你能够用本房的钥匙。你也能够指定
用还有一个房子的钥匙才干开。这种话,你要跑到还有一栋房子那儿把那个钥匙拿来,并用那个房子的钥匙来打开这个房子的带锁的屏风。
记住你获得的那还有一栋房子的钥匙,并不影响其它人进入那栋房子没有锁的房间。
为什么要使用同步代码块呢?我想应该是这种:首先对程序来讲同步的部分非常影响执行效率,而一个方法一般是先创建一些局部变
量,再对这些变量做一些 操作,如运算,显示等等;而同步所覆盖的代码越多。对效率的影响就越严重。因此我们通常尽量缩小其影响范围。
怎样做?同步代码块。我们仅仅把一个方法中该同 步的地方同步。比方运算。
另外,同步代码块能够指定钥匙这一特点有个额外的优点,是能够在一定时期内霸占某个对象的key。还记得前面说过一般情况下钥
匙的使用原则吗。
如今不是一般情况了。你所取得的那把钥匙不是永远不还,而是在退出同步代码块时才还。
还用前面那个想连续用两个上锁房间的家伙打例如。如何才干在用完一间以后。继续使用还有一间呢。
用同步代码块吧。先创建另外
一个线程,做一个同步代码 块,把那个代码块的锁指向这个房子的钥匙。然后启动那个线程。仅仅要你能在进入那个代码块时抓到这房子的钥匙
。你就能够一直保留到退出那个代码块。也就是说 你甚至能够对本房内全部上锁的房间遍历。甚至再sleep(10*60*1000),而房门口却还有
1000个线程在等这把钥匙呢。非常过瘾吧。
在此对sleep()方法和钥匙的关联性讲一下。一个线程在拿到key后,且没有完毕同步的内容时,假设被强制sleep()了。那key还一
直在 它那儿。
直到它再次执行。做全然部同步内容,才会归还key。记住。那家伙仅仅是干活干累了,去歇息一下,他并没干完他要干的事。为
了避免别人进入那个房间 把里面搞的一团糟。即使在睡觉的时候他也要把那唯一的钥匙戴在身上。
最后,或许有人会问。为什么要一把钥匙通开,而不是一个钥匙一个门呢?我想这纯粹是由于复杂性问题。一个钥匙一个门当然更
安全。可是会牵扯好多问题。
钥匙 的产生。保管,获得,归还等等。其复杂性有可能随同步方法的添加呈几何级数添加,严重影响效率。这也
算是一个权衡的问题吧。为了添加一点点安全性。导致效 率大大减少,是多么不可取啊。
synchronized的一个简单样例
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--);
}
}
}
}
上面的样例中为了制造一个时间差,也就是出错的机会,使用了Thread.sleep(10)
Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronizedkeyword就能够轻松地解决多线程共享数据同步问题。究竟如
何?――还得对synchronizedkeyword的作用进行深入了解才可定论。
总的说来,synchronizedkeyword能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。
假设再细的分类,
synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
在进一步阐述之前。我们须要明白几点:
A.不管synchronizedkeyword加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――并且同步方法非常可能还会被其
他线程的对象訪问。
B.每一个对象仅仅有一个锁(lock)与之相关联。
C.实现同步是要非常大的系统开销作为代价的。甚至可能造成死锁,所以尽量避免无谓的同步控制。
接着来讨论synchronized用到不同地方对代码产生的影响:
如果P1、P2是同一个类的不同对象,这个类中定义了下面几种情况的同步块或同步方法,P1、P2就都能够调用它们。
1. 把synchronized当作函数修饰符时,演示样例代码例如以下:
Public synchronized void methodAAA()
{
//….
}
这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。
也就是说,当一个对象P1在不同的线程中
运行这个同步方法时,它们之间会形成相互排斥,达到同步的效果。可是这个对象所属的Class所产生的还有一对象P2却能够随意调用这个被加了
synchronizedkeyword的方法。
上边的演示样例代码等同于例如以下代码:
public void methodAAA()
{
synchronized (this) // (1)
{
//…..
}
}
(1)处的this指的是什么呢?它指的就是调用这种方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。――那个
拿到了P1对象锁的线程,才干够调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这样的情形下摆脱同步机制的控制,造
成数据混乱:(
2.同步块。演示样例代码例如以下:
public void method3(SomeObject so)
{
synchronized(so)
{
//…..
}
}
这时,锁就是so这个对象,谁拿到这个锁谁就能够执行它所控制的那段代码。当有一个明白的对象作为锁时。就能够这样敲代码,但当没有明
确的对象作为锁,仅仅是想让一段代码同步时。能够创建一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable
{
private byte[] lock = new byte[0]; // 特殊的instance变量
Public void methodA()
{
synchronized(lock) { //… }
}
//…..
}
注:零长度的byte数组对象创建起来将比不论什么对象都经济――查看编译后的字节码:生成零长度的byte[]对象仅仅需3条操作码。而Object lock
= new Object()则须要7行操作码。
3.将synchronized作用于static 函数,演示样例代码例如以下:
Class Foo
{
public synchronized static void methodAAA() // 同步的static 函数
{
//….
}
public void methodBBB()
{
synchronized(Foo.class) // class literal(类名称字面常量)
}
}
代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁非常特别,是当前调用这
个方法的对象所属的类(Class,而不再是由这个Class产生的某个详细对象了)。
记得在《Effective Java》一书中看到过将 Foo.class和 P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的
目的。
P1指的是由Foo类产生的对象。
能够判断:假设一个类中定义了一个synchronized的static函数A。也定义了一个synchronized 的instance函数B。那么这个类的同一对象Obj
在多线程中分别訪问A和B两个方法时,不会构成同步。由于它们的锁都不一样。
A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
小结例如以下:
搞清楚synchronized锁定的是哪个对象。就能帮助我们设计更安全的多线程程序。
另一些技巧能够让我们对共享资源的同步訪问更加安全:
1. 定义private 的instance变量+它的 get方法。而不要定义public/protected的instance变量。假设将变量定义为public,对象在外界能够
绕过同步方法的控制而直接取得它,并修改它。
这也是JavaBean的标准实现方式之中的一个。
2. 假设instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全。由于当外界对象通过get方法拿到这个instance对象
的引用后,又将其指向还有一个对象。那么这个private变量也就变了。岂不是非常危急。 这个时候就须要将get方法也加上synchronized同步。并
且,仅仅返回这个private对象的clone()――这样。调用端得到的就是对象副本的引用了