您的位置:首页 > 编程语言 > Java开发

Java 的 synchronized 是可重入性锁

2016-04-18 13:15 483 查看
当线程请求一个由其它线程持有的对象锁时,该线程会阻塞,而当线程请求由自己持有的对象锁时,如果该锁是重入锁,请求就会成功,否则阻塞.

我们来看看synchronized, 它拥有强制原子性的内置锁机制是一个重入锁, 所以在使用synchronized时, 当一个线程请求得到一个对象锁后再次请求此对象锁,

可以再次得到该对象锁,就是说在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以拿到锁,如下:

01
public
class
Child
extends
Father
{
02
public
static
void
main(String[]
args) {
03
Child
child =
new
Child();
04
child.doSomething();
05
}
06
07
public
synchronized
void
doSomething()
{
08
System.out.println(
"child.doSomething()"
);
09
doAnotherThing();
//
调用自己类中其他的synchronized方法
10
 
11
}
12
13
private
synchronized
void
doAnotherThing()
{
14
super
.doSomething();
//
调用父类的synchronized方法
15
System.out.println(
"child.doAnotherThing()"
);
16
}
17
}
18
19
class
Father
{
20
public
synchronized
void
doSomething()
{
21
System.out.println(
"father.doSomething()"
);
22
}
23
}
运行结果:

child.doSomething()

father.doSomething()

child.doAnotherThing()

这里的对象锁只有一个,就是child对象的锁,当执行child.doSomething时,该线程获得child对象的锁,在doSomething方法内执行doAnotherThing时再次请求child对象的锁,因为synchronized是重入锁,所以可以得到该锁,继续在doAnotherThing里执行父类的doSomething方法时第三次请求child对象的锁,同理可得到,如果不是重入锁的话,那这后面这两次请求锁将会被一直阻塞,从而导致死锁。

所以在java内部,同一线程在调用自己类中其他synchronized方法/块或调用父类的synchronized方法/块都不会阻碍该线程的执行,就是说同一线程对同一个对象锁是可重入的,而且同一个线程可以获取同一把锁多次,也就是可以多次重入。因为java线程是基于“每线程(per-thread)”,而不是基于“每调用(per-invocation)”的(java中线程获得对象锁的操作是以每线程为粒度的,per-invocation互斥体获得对象锁的操作是以每调用作为粒度的)

我们再来看看重入锁是怎么实现可重入性的,其实现方法是为每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放该锁。

我们再来看看下例子加深一下理解:

01
public
class
Child
extends
Father
{
02
public
static
void
main(String[]
args) {
03
Child
child =
new
Child();
04
child.doSomething();
05
}
06
07
public
void
doSomething()
{
08
while
(i>
0
){
09
System.out.println(
"child.doSomething(),i="
+i--);
10
super
.doSomething();
11
doSomething();
12
}
13
}
14
}
15
16
class
Father
{
17
int
i
=
6
;
18
public
synchronized
void
doSomething()
{
19
System.out.println(
"father.doSomething(),i="
+i--);
20
}
21
}
运行结果:
child.doSomething(),i=6

father.doSomething(),i=5

child.doSomething(),i=4

father.doSomething(),i=3

child.doSomething(),i=2

father.doSomething(),i=1

再看:

01
import
java.util.ArrayList;
02
03
public
class
ReentrancyTest
{
04
static
public
int
i
=
1
;
05
public
int
count
;
06
07
public
ReentrancyTest()
{
08
super
();
09
}
10
11
public
static
void
main(String[]
args) {
12
int
threadNum
=
10
;
//
设置10个线程同时执行
13
//
每个线程都关联同一个ReentrancyTest对象
14
ReentrancyTest
reentrancyTest =
new
ReentrancyTest();
15
ArrayList<MyThread>
threadList =
new
ArrayList<MyThread>();
16
 
//
为10个线程赋值同一个ReentrancyTest对象的引用
17
for
(
int
i
=
0
;
i <threadNum;i++)
18
{
19
MyThread
myThread =
new
MyThread();
20
myThread.reentrancyTest
= reentrancyTest;
21
threadList.add(myThread);
22
}
23
 
//
启动10个线程
24
for
(
int
i
=
0
;
i <threadNum;i++) {
25
new
Thread((MyThread)
threadList.get(i)).start();
26
}
27
}
28
29
public
void
doSomething()
{
30
//随机产生一个睡眠时间
31
int
sleep=(
int
)(Math.random()*
500
);
32
try
{
33
Thread.sleep(sleep);
34
}
catch
(InterruptedException
e) {
35
e.printStackTrace();
36
}
37
count
= i++;
38
System.out.println(
"第"
+count+
"个线程:"
+Thread.currentThread().getName()
39
+
"进入到doSomething()执行代码--睡眠"
+sleep+
"毫秒"
);
40
try
{
41
Thread.sleep(sleep);
42
}
catch
(InterruptedException
e) {
43
e.printStackTrace();
44
}
45
System.out.println(Thread.currentThread().getName()+
"线程执行doSomething()完毕,睡眠时间:"
46
+
sleep+
",已进入doSomething执行代码的线程总数为:"
+count);
47
}
48
}
49
50
class
MyThread
extends
Thread
{
51
public
ReentrancyTest
reentrancyTest;
52
53
public
MyThread()
{
54
super
();
55
}
56
57
@Override
58
public
void
run()
{
59
reentrancyTest.doSomething();
60
super
.run();
61
}
62
}
运行结果:

第1个线程:Thread-16进入到doSomething()执行代码--睡眠7毫秒

Thread-16线程执行doSomething()完毕,睡眠时间:7,已进入doSomething执行代码的线程总数为:1

第2个线程:Thread-10进入到doSomething()执行代码--睡眠154毫秒

第3个线程:Thread-15进入到doSomething()执行代码--睡眠221毫秒

第4个线程:Thread-17进入到doSomething()执行代码--睡眠222毫秒

第5个线程:Thread-14进入到doSomething()执行代码--睡眠276毫秒

Thread-10线程执行doSomething()完毕,睡眠时间:154,已进入doSomething执行代码的线程总数为:5

第6个线程:Thread-19进入到doSomething()执行代码--睡眠340毫秒

第7个线程:Thread-13进入到doSomething()执行代码--睡眠367毫秒

第8个线程:Thread-12进入到doSomething()执行代码--睡眠404毫秒

Thread-15线程执行doSomething()完毕,睡眠时间:221,已进入doSomething执行代码的线程总数为:8

Thread-17线程执行doSomething()完毕,睡眠时间:222,已进入doSomething执行代码的线程总数为:8

第10个线程:Thread-11进入到doSomething()执行代码--睡眠451毫秒

第10个线程:Thread-18进入到doSomething()执行代码--睡眠451毫秒

Thread-14线程执行doSomething()完毕,睡眠时间:276,已进入doSomething执行代码的线程总数为:10

Thread-19线程执行doSomething()完毕,睡眠时间:340,已进入doSomething执行代码的线程总数为:10

Thread-13线程执行doSomething()完毕,睡眠时间:367,已进入doSomething执行代码的线程总数为:10

Thread-12线程执行doSomething()完毕,睡眠时间:404,已进入doSomething执行代码的线程总数为:10

Thread-11线程执行doSomething()完毕,睡眠时间:451,已进入doSomething执行代码的线程总数为:10

Thread-18线程执行doSomething()完毕,睡眠时间:451,已进入doSomething执行代码的线程总数为:10

由此可见多线程在操作同一对象时,如果对象中的函数不是同步的,多线程可以并发执行此函数

如果把doSomething方法加上synchronized同步后的结果变为:

第1个线程:Thread-10进入到doSomething()执行代码--睡眠208毫秒

Thread-10线程执行doSomething()完毕,睡眠时间:208,已进入doSomething执行代码的线程总数为:1

第2个线程:Thread-19进入到doSomething()执行代码--睡眠124毫秒

Thread-19线程执行doSomething()完毕,睡眠时间:124,已进入doSomething执行代码的线程总数为:2

第3个线程:Thread-17进入到doSomething()执行代码--睡眠176毫秒

Thread-17线程执行doSomething()完毕,睡眠时间:176,已进入doSomething执行代码的线程总数为:3

第4个线程:Thread-18进入到doSomething()执行代码--睡眠43毫秒

Thread-18线程执行doSomething()完毕,睡眠时间:43,已进入doSomething执行代码的线程总数为:4

第5个线程:Thread-15进入到doSomething()执行代码--睡眠98毫秒

Thread-15线程执行doSomething()完毕,睡眠时间:98,已进入doSomething执行代码的线程总数为:5

第6个线程:Thread-16进入到doSomething()执行代码--睡眠360毫秒

Thread-16线程执行doSomething()完毕,睡眠时间:360,已进入doSomething执行代码的线程总数为:6

第7个线程:Thread-13进入到doSomething()执行代码--睡眠286毫秒

Thread-13线程执行doSomething()完毕,睡眠时间:286,已进入doSomething执行代码的线程总数为:7

第8个线程:Thread-14进入到doSomething()执行代码--睡眠442毫秒

Thread-14线程执行doSomething()完毕,睡眠时间:442,已进入doSomething执行代码的线程总数为:8

第9个线程:Thread-11进入到doSomething()执行代码--睡眠483毫秒

Thread-11线程执行doSomething()完毕,睡眠时间:483,已进入doSomething执行代码的线程总数为:9

第10个线程:Thread-12进入到doSomething()执行代码--睡眠447毫秒

Thread-12线程执行doSomething()完毕,睡眠时间:447,已进入doSomething执行代码的线程总数为:10

所以不同线程对同一对象锁是要竞争的,是同步阻塞模式,不能像同一线程对同一对象锁是可重入的!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: