Java提供了强制原子性的内部锁机制:synchronized块。但是内部锁是可重入的,当线程试图获得它自己占有的锁时,请求会成功。
简单的说,就是在一个synchronized方法内部调用本类的其他synchronized方法时,永远可以拿到锁。
如例子1.1所示
public class LoggingWidget extends Widget{
public static void main(String[] args) {
LoggingWidget lw=new LoggingWidget();
lw.doSomething();
}
public synchronized void doSomething(){
System.out.println("LoggingWidget->doSomething()");
doAnotherThing(); //调用自己类中其他的synchronized方法
super.doSomething(); //调用父类的synchronized方法
}
private synchronized void doAnotherThing(){
System.out.println("LoggingWidget->doAnotherThing()");
}
}
class Widget{
public synchronized void doSomething(){
System.out.println("Widget->doSomething()");
}
}
执行结果是:
LoggingWidget->doSomething()
LoggingWidget->doAnotherThing()
Widget->doSomething()
可见,在java内部,调用父类的synchronized方法和调用自己类中其他synchronized方法都不会阻碍该程序的运行,正是因为java线程是基于“每线程(per-thread)”,而不是基于“每调用的(per-invocation)”的。重进入的实现是通过为每个锁关联一个请求计数和一个占有它的线程。
更有甚者会出现无限递归的情况:
public class reentry{
int i=0;
public static void main(String[] args) {
reentry lw=new reentry();
lw.doSomething();
}
public synchronized void doSomething(){
System.out.println("doSomething()"+i++);
doSomething(); //synchronized方法递归调用
}
}
输出结果:
......
doSomething()3831
doSomething()3832
doSomething()3833
Exception in thread "main" java.lang.StackOverflowError
......
可以看到,程序进入了死循环,直到堆栈溢出。但是不会将锁释放出去。
=============================================================
要记住一句话“重进入的实现是通过为每个锁关联一个请求计数和一个占有它的线程”,就是说如果有其他线程调用该对象的某个方法,那么该对象的其他synchronized方法并不能与其重进入,而是互斥,因为占有该函数的进程不一样,看如下例子:
public class LoggingWidget {
static public int i=0;
public int ii=0;
public LoggingWidget() {
super();
}
public static void main(String[] args) {
int totalNumOfThread=20; //有20个线程同时执行
LoggingWidget lw=new LoggingWidget(); //每个线程都关联同一个LoggingWidget对象
ArrayList<outer> o=new ArrayList<outer>();
for(int s=0;s<totalNumOfThread;s++) //为20个线程赋值同一个LoggingWidget对象的引用
{
outer t=new outer();
t.lw=lw;
o.add(t);
}
for(int s=0;s<totalNumOfThread;s++)
{
new Thread((outer)o.get(s)).start(); //启动20个线程
}
}
public void doSomething(){ //注意,这里没有给方法synchronized属性
int sleep=(int)(Math.random()*500); //随机产生一个睡眠时间
try {
Thread.sleep(sleep); //睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
ii=i++; //为每个线程赋予一个ID,ID自增
try {
Thread.sleep(sleep); //继续睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ii+"线程执行LoggingWidget->doSomething(),睡眠时间:"+sleep);
}
}
class outer extends Thread
{
public LoggingWidget lw;
public outer() {
super();
}
@Override
public void run() {
lw.doSomething();
super.run();
}
}
由于没有给方法synchronized属性,所以输出结果如下:
1线程执行LoggingWidget->doSomething(),睡眠时间:4
1线程执行LoggingWidget->doSomething(),睡眠时间:4
3线程执行LoggingWidget->doSomething(),睡眠时间:56
8线程执行LoggingWidget->doSomething(),睡眠时间:108
11线程执行LoggingWidget->doSomething(),睡眠时间:151
11线程执行LoggingWidget->doSomething(),睡眠时间:157
12线程执行LoggingWidget->doSomething(),睡眠时间:177
13线程执行LoggingWidget->doSomething(),睡眠时间:192
14线程执行LoggingWidget->doSomething(),睡眠时间:213
16线程执行LoggingWidget->doSomething(),睡眠时间:218
17线程执行LoggingWidget->doSomething(),睡眠时间:232
19线程执行LoggingWidget->doSomething(),睡眠时间:280
19线程执行LoggingWidget->doSomething(),睡眠时间:354
19线程执行LoggingWidget->doSomething(),睡眠时间:358
19线程执行LoggingWidget->doSomething(),睡眠时间:401
19线程执行LoggingWidget->doSomething(),睡眠时间:428
19线程执行LoggingWidget->doSomething(),睡眠时间:437
19线程执行LoggingWidget->doSomething(),睡眠时间:455
19线程执行LoggingWidget->doSomething(),睡眠时间:468
19线程执行LoggingWidget->doSomething(),睡眠时间:498
如果方法public void doSomething()改成public synchronized void doSomething(),那么输出结果为:
0线程执行LoggingWidget->doSomething(),睡眠时间:384
1线程执行LoggingWidget->doSomething(),睡眠时间:26
2线程执行LoggingWidget->doSomething(),睡眠时间:391
3线程执行LoggingWidget->doSomething(),睡眠时间:289
4线程执行LoggingWidget->doSomething(),睡眠时间:266
5线程执行LoggingWidget->doSomething(),睡眠时间:248
6线程执行LoggingWidget->doSomething(),睡眠时间:121
7线程执行LoggingWidget->doSomething(),睡眠时间:395
8线程执行LoggingWidget->doSomething(),睡眠时间:454
9线程执行LoggingWidget->doSomething(),睡眠时间:457
10线程执行LoggingWidget->doSomething(),睡眠时间:181
11线程执行LoggingWidget->doSomething(),睡眠时间:170
12线程执行LoggingWidget->doSomething(),睡眠时间:470
13线程执行LoggingWidget->doSomething(),睡眠时间:444
14线程执行LoggingWidget->doSomething(),睡眠时间:114
15线程执行LoggingWidget->doSomething(),睡眠时间:4
16线程执行LoggingWidget->doSomething(),睡眠时间:40
17线程执行LoggingWidget->doSomething(),睡眠时间:320
18线程执行LoggingWidget->doSomething(),睡眠时间:416
19线程执行LoggingWidget->doSomething(),睡眠时间:148
可见,不同线程调用同一个对象的synchronized方法,是不会重进入的,而是产生互斥锁.