读Java并发编程的疑惑

最近又开始咀嚼以前狼吞虎咽过的《java并发编程实践》,看到一段突然理解不了,发上来大家瞅瞅
是关于sychronized可重入的。
引用

   当一个线程请求其他线程已经占有的锁时,请求线程将被阻塞。然而内部锁是可重入的,因此线程在试图获得它它自己占有的锁时,请求会成功。重进入意味着所有的请求是基于“每线程(per-thread)”的,而不是基于“每调用(per-invocation)”的。重进入的

实现是通过为每个锁关联一个请求计数和一个占有它的线程。当计数为0时,认为锁是未被占有的。线程请求一个未被占有的锁时,JVM将记录锁的占有者,且将请求计数置为1.如果同一线程再次请求这个锁,计数将递增;每次占用线程退出同步块,计数器值将递减。直到

计数器达到0时,锁被释放。
   重进入方便了锁行为的封装,因此简化了面向对象并发代码的开发。如下所示,子类覆盖了父类sychronized类型的方法,并调用父类中的方法。如果没有可重入的锁,这段看上去很自然的代码就会产生死锁。因为Widget和LogginWidget中的doSomething方法都是

sychronized类型的,都会在处理前试图获得Widget的锁,因为锁已经被占有,导致线程会永久地延迟,等待着一个永远无法获得的锁。重进入帮助我们避免了这种死锁。

 public class Widget{
   public sychronized void doSomething(){
      ...
   }
 }
 
 public class LogginWidget extends Widget{
    public sychronized void doSomething(){
       System.out.println(toString() + ": calling doSomething");
       super.doSomething();
    }
 }


而书中前部分说到:
引用
Java提供了强制原子性的内置锁机制:sychronized块。一个sychronized块有两部分:锁对象引用,以及这个锁保护的代码块。sychronized方法是对跨越了整个方法体的sychronized块的简短描述,至于sychronized方法的锁,就是该方法所在的对象本身。(静

态的sychronized方法从Class对象上获取锁)


让我产生了一些想法:
引用

因为Widget和LogginWidget中的doSomething方法都是sychronized类型的,都会在处理前试图获得Widget的锁

这里只有LogginWidget这个实例,而父类中的锁定实际上应该是锁定到子类中。为何文中说会获得Widget的锁,有点耐人寻味?

 public class Widget{
   public synchronized static void doSomething(){
      ...
   }
 }
 
 public class LogginWidget extends Widget{
    //不覆盖
    public sychronized void doSomething2(){
       System.out.println(toString() + ": calling doSomething");
       doSomething();
    }
 }



此处LogginWidget实例doSomething2方法的获得锁是怎么样的?
super.doSomething()这句的锁对象是谁?
Widget的Class作为锁对象还是LogginWidget的Class作为了锁对象?
根据类似方法的绑定策略:
static方法有父类静态绑定,也就是说子类无法覆盖父类的静态方法。
实例方法则在子类中动态绑定,也就是子类可以覆盖父类的实例方法。

那么是否这个对象锁应该是Widget的Class对象呢?那是不是doSomething2的方法的执行会锁定两个对象呢,一个是Widget的Class一个是LogginWidget自身实例。

为此做了一个试验:
public class Sync {
	
	public synchronized static void doSomething(String name) throws InterruptedException{
		System.out.println(name + " super.doSomething, will sleep 10s");
		Thread.sleep(10000);
		System.out.println(name + " wake up");
	}

        public synchronized static void test(String name){
		System.out.println(name + " super.test");
	}
	
	public static void main(String[] args) throws InterruptedException{
		final SubSync sub = new SubSync();
		Thread t1 = new Thread(new Task(sub,"t1"));
		Thread t2 = new Thread(new Runnable(){

			@Override
			public void run() {
				try {
					Sync.doSomething("t2");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
		});
		t1.start();
		t2.start();
		
	}

}

class Task implements Runnable{
	
	private SubSync sync;
	
	private String name;
	
	public Task(SubSync sync,String name){
	   this.sync = 	sync;
	   this.name = name;
	}

	@Override
	public void run() {
		
		try {
			System.out.println(name + " stated.");
			sync.doSomething2(name);
			System.out.println(name + " finished.");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}

class SubSync extends Sync{
	public synchronized void doSomething2(String name) throws InterruptedException{
		System.out.println(name + " sub.doSomething2");
		doSomething(name);
	}
}


输出结果为:
t1 stated.
t1 sub.doSomething2
t1 super.doSomething, will sleep 10s
t2 begin
t1 wake up
t1 finished.
t2 super.test

静态方法是锁住了Sync类的Class实例。so,SubSync方法doSomething2执行时实际上是锁定了两个对象,一个是Sync的Class,一个是SubSync实例本身!
那这样死锁的机会就会增加很多。
因为同时要锁住两个对象,就存在着相互等待的情况,有可能一个线程持有了一个同步方法中的其中一个锁,而另一个线程持有了该同步方法中的另一个锁,两个线程相互等待对方释放锁以便自己获得。

你可能感兴趣的:(java,jvm,thread,编程,UP)