java wow 单无双
BlogJava
|
首页
|
发新随笔
|
发新文章
|
联系
|
聚合
|
管理
【收藏贴】Java 多线程同步问题的探究(二、给我一把锁,我能创造一个规矩)
在上一篇中,我们讲到了多线程是如何处理共享资源的,以及保证他们对资源进行互斥访问所依赖的重要机制:对象锁。
本篇中,我们来看一看传统的同步实现方式以及这背后的原理。
很多人都知道,在Java多线程编程中,有一个重要的关键字,synchronized。但是很多人看到这个东西会感到困惑:“都说同步机制是通过对象锁来实现的,但是这么一个关键字,我也看不出来Java程序锁住了哪个对象阿?“
没错,我一开始也是对这个问题感到困惑和不解。不过还好,我们有下面的这个例程:
public
class
ThreadTest
extends
Thread
{
2
3
private
int
threadNo;
4
5
public
ThreadTest(
int
threadNo)
{
6
this
.threadNo
=
threadNo;
7
}
8
9
public
static
void
main(String[] args)
throws
Exception
{
10
for
(
int
i
=
1
; i
<
10
; i
++
)
{
11
new
ThreadTest(i).start();
12
Thread.sleep(
1
);
13
}
14
}
15
16
@Override
17
public
synchronized
void
run()
{
18
for
(
int
i
=
1
; i
<
10000
; i
++
)
{
19
System.out.println(
"
No.
"
+
threadNo
+
"
:
"
+
i);
20
}
21
}
22
}
这个程序其实就是让10个线程在控制台上数数,从1数到9999。理想情况下,我们希望看到一个线程数完,然后才是另一个线程开始数数。但是这个程序的执行过程告诉我们,这些线程还是乱糟糟的在那里抢着报数,丝毫没有任何规矩可言。
但是细心的读者注意到:run方法还是加了一个synchronized关键字的,按道理说,这些线程应该可以一个接一个的执行这个run方法才对阿。
但是通过上一篇中,我们提到的,对于一个成员方法加synchronized关键字,这实际上是以这个成员方法所在的对象本身作为对象锁。在本例中,就是以ThreadTest类的一个具体对象,也就是该线程自身作为对象锁的。一共十个线程,每个线程持有自己 线程对象的那个对象锁。这必然不能产生同步的效果。换句话说,
如果要对这些线程进行同步,那么这些线程所持有的对象锁应当是共享且唯一的!
我们来看下面的例程:
public
class
ThreadTest2
extends
Thread
{
2
3
private
int
threadNo;
4
private
String lock;
5
6
public
ThreadTest2(
int
threadNo, String lock)
{
7
this
.threadNo
=
threadNo;
8
this
.lock
=
lock;
9
}
10
11
public
static
void
main(String[] args)
throws
Exception
{
12
String lock
=
new
String(
"
lock
"
);
13
for
(
int
i
=
1
; i
<
10
; i
++
)
{
14
new
ThreadTest2(i, lock).start();
15
Thread.sleep(
1
);
16
}
17
}
18
19
public
void
run()
{
20
synchronized
(lock)
{
21
for
(
int
i
=
1
; i
<
10000
; i
++
)
{
22
System.out.println(
"
No.
"
+
threadNo
+
"
:
"
+
i);
23
}
24
}
25
}
26
}
我们注意到,该程序通过在main方法启动10个线程之前,创建了一个String类型的对象。并通过ThreadTest2的构造函数,将这个对象赋值给每一个ThreadTest2线程对象中的私有变量lock。根据Java方法的传值特点,我们知道,这些线程的lock变量实际上指向的是堆内存中的同一个区域,即存放main函数中的lock变量的区域。
程序将原来run方法前的synchronized关键字去掉,换用了run方法中的一个synchronized块来实现。这个同步块的对象锁,就是main方法中创建的那个String对象。换句话说,他们指向的是同一个String类型的对象,对象锁是共享且唯一的!
于是,我们看到了预期的效果:10个线程不再是争先恐后的报数了,而是一个接一个的报数。
再来看下面的例程:
1
public
class
ThreadTest3
extends
Thread
{
2
3
private
int
threadNo;
4
private
String lock;
5
6
public
ThreadTest3(
int
threadNo)
{
7
this
.threadNo
=
threadNo;
8
}
9
10
public
static
void
main(String[] args)
throws
Exception
{
11
//
String lock = new String("lock");
12
for
(
int
i
=
1
; i
<
20
; i
++
)
{
13
new
ThreadTest3(i).start();
14
Thread.sleep(
1
);
15
}
16
}
17
18
public
static
synchronized
void
abc(
int
threadNo)
{
19
for
(
int
i
=
1
; i
<
10000
; i
++
)
{
20
21
System.out.println(
"
No.
"
+
threadNo
+
"
:
"
+
i);
22
23
24
25
26
}
27
}
28
29
public
void
run()
{
30
abc(threadNo);
31
}
32
}
细心的读者发现了:这段代码没有使用main方法中创建的String对象作为这10个线程的线程锁。而是通过在run方法中调用本线程中一个静态的同步方法abc而实现了线程的同步。我想看到这里,你们应该很困惑:这里synchronized静态方法是用什么来做对象锁的呢?
我们知道,对于同步静态方法,对象锁就是该静态放发所在的类的Class实例,由于在JVM中,所有被加载的类都有唯一的类对象,具体到本例,就是唯一的ThreadTest3.class对象。不管我们创建了该类的多少实例,但是它的类实例仍然是一个!
这样我们就知道了:
1、对于同步的方法或者代码块来说,必须获得对象锁才能够进入同步方法或者代码块进行操作;
2、如果采用method级别的同步,则对象锁即为method所在的对象,如果是静态方法,对象锁即指method所在的
Class对象(唯一);
3、对于代码块,对象锁即指synchronized(abc)中的abc;
4、因为第一种情况,对象锁即为每一个线程对象,因此有多个,所以同步失效,第二种共用同一个对象锁lock,因此同步生效,第三个因为是
static因此对象锁为ThreadTest3的class 对象,因此同步生效。
如上述正确,则同步有两种方式,同步块和同步方法(为什么没有wait和notify?这个我会在补充章节中做出阐述)
如果是同步代码块,则对象锁需要编程人员自己指定,一般有些代码为synchronized(this)只有在单态模式才生效;
(本类的实例有且只有一个)
如果是同步方法,
则分静态和非静态两种
。
静态方法则一定会同步,非静态方法需在单例模式才生效,推荐用静态方法(不用担心是否单例)。
所以说,在Java多线程编程中,最常见的synchronized关键字实际上是依靠对象锁的机制来实现线程同步的。
我们似乎可以听到synchronized在向我们说:“给我
一把
锁,我能创造一个规矩”。
下一篇中,我们将看到JDK 5提供的新的同步机制,也就是大名鼎鼎的Doug Lee提供的Java Concurrency框架。
发表于 2010-03-15 00:15
程序员仓库
阅读(156)
评论(0)
编辑
收藏
新用户注册
刷新评论列表
只有注册用户
登录
后才能发表评论。
网站导航:
博客园
IT新闻
Chat2DB
C++博客
博问
管理
随笔:3 文章:0 评论:0 引用:0
<
2010年3月
>
日
一
二
三
四
五
六
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
常用链接
我的随笔
我的评论
我的参与
留言簿
给我留言
查看公开留言
查看私人留言
随笔档案
2010年3月 (2)
2010年1月 (1)
搜索
最新评论
阅读排行榜
1. 【收藏贴】Java 多线程同步问题的探究(二、给我一把锁,我能创造一个规矩) (156)
2. 【收藏贴】Java多线程同步问题的探究(一、线程的先来后到) (120)
3. cookie和session的工作机制(106)
评论排行榜
1. 【收藏贴】Java 多线程同步问题的探究(二、给我一把锁,我能创造一个规矩) (0)
2. 【收藏贴】Java多线程同步问题的探究(一、线程的先来后到) (0)
3. cookie和session的工作机制(0)