Java
关键字
synchronized
是
Java
语言提供的对多线程和同步的一种机制。
synchronized
可以作为函数的修饰符,也可作为函数内的语句。它可以作用于
instance
变量,对象引用(
object reference
),
static
函数和类名称字面常量(
class literals
)。
下面介绍一下
synchronized
的关键字的使用方法:
一、
synchronized
作函数修饰符
public
synchronized
void
fun()
{
……..
}
fun()
就是一个同步方法,此时
synchronized
关键字锁定的是调用这个同步方法的对象。假设有
p1
和
p2
是同一个类的两个对象,
p1
在不同的线程中运行会对
fun()
产生互斥和同步的效果;但是
p2
对象和
p1
对象互相不会对
fun()
产生同步和互斥作用(当然,不同线程中的
p2
对象的
fun()
还是有同步和互斥)。
对于非
static
的情况,
synchronized
是对象级别的,其实质是将
synchronized
作用于对象引用(
object reference
)上,即拿到
p1
对象锁的线程,对
p1
的
fun()
方法有同步互斥作用,不同的对象之间坚持“和平共处”。因此,上面的代码等同于:
public
void
fun()
{
synchronized(
this
)
//
this指的是调用这个方法的对象
{
……
}
}
二、
synchronized
同步程序块
public
void
fun_1(someObject obj)
{
synchronized
(obj)
{
……
}
}
上面代码中,锁住的是
obj
对象(正如前面说的,对于非
static
的情况,
sysnchronized
是对象级别的),谁拿到这个锁,谁就可运行
obj
控制的那段代码。通常情况,如果我们知道对用哪个对象作为锁时,就可以像上面的代码块一样使用
synchronized
。假如没有明确的对象作为锁,程序员又希望同步一段代码块,就可以使用下面的
trick
。
class
Test
implements
Runnable
{
private
byte
[] lock
=
new
byte
[
0
];
//
定一个instance变量
public
void
fun_2()
{
synchronized
(lock)
{
……
}
}
}
在上面的代码示例中,定义了一个特殊的
instance
变量作为锁,这个
instance
变量必须是一个对象。定义
lock
为长度为
0
的数组对象是最佳方案。在编译后的字节码中,生成长度为
0
的
byte[]
只需要三条操作码。假如我们用所有类的超类
Object
来作锁,需要生成七条操作码。
[
注意:
]
如果需要定义特殊的
instance
变量作为锁,最好将其定义为
private
的,同时定义其
get()
方法(如果使用自己定义的类的对象作为锁)。如果变量是
public
的,其他类的对象可以得到这个锁的控制权,并修改这个锁。这是非常不安全的。
[
注意:
]
如果
instance
变量是一个对象,如数组或
ArrayList
什么的,那上述方法仍然不安全,因为当外界对象通过
get
方法拿到这个
instance
对象的引用后,又将其指向另一个对象,那么这个
private
变量也就变了,岂不是很危险。这个时候就需要将
get
方法也加上
synchronized
同步,并且,只返回这个
private
对象的
clone()――
这样,调用端得到的就是对象副本的引用了。
三、
synchronized
修饰
static
方法
synchronized
静态
(static)
方法的用法如下面代码示例:
public
static
synchronized
void
fun_3()
{
……
}
如果方法用
static
修饰,
synchronized
的作用范围就是
class
一级的,它对类的所有对象起作用。像第一点中的
fun()
方法,如果是
static
的,那么
synchronized
对
p1
对象和
p2
对象都起到同步互斥的作用。
其实说白了,对于
static
的
synchronize
也可以理解为对象级别的(
hoho
!是不是有点糊涂了?
^_^
),因此下面代码可以达到同样效果:
class
XX
{
public
static
void
fun_4()
{
synchronized
(XX.
class
)
{
……
}
}
}
请注意,
XX.class
也是一个对象,类型是
Class
,在一个
ClassLoader
里,它是唯一的。因此,我在前面说,也理解它是对象级别的。
最后简单总结一下:
(1)
通常把
synchronized
关键字的作用范围划分为类的范围和对象的范围两种,不过偶个人理解的是,既然取得的锁都是对象(参考第三点),也可以认为
synchronized
关键字都作用于对象。
(2)
synchronized
关键字是不能继承的,即,父类的
synchronized
方法在子类中不是
synchronized
,必须要重新的显式的声明为
synchronized
才行。
(3)
实现同步需要很大的系统开销,导致延迟等待,甚至可能造成死锁,所以在非多线程情况下不要使用。