每个java对象都可以隐式地扮演一个用于同步的锁的角色,这些内置的锁被称做内部锁。内部锁在java中扮演了互斥锁的角色,意味着至多只有一个线程可以拥有锁,当线程A尝试请求一个被线程B占有的锁是,线程A必须等待或者阻塞,知道B释放他。如果B永远不释放锁,A将永远等下去。
重进入
当一个线程请求其他线程已经占有的锁时,请求线程将被阻塞。然而内部锁是可重进入的,因此线程在试图获得它自己占有的锁时,请求会成功。重进入意味着所有的请求是基于“每个线程”,而不是基于“每调用”的。重进入的实现是通过为每个锁关联一个请求计数和一个占有它的线程。当计数为0时,认为锁没有被占有的。线程请求一个未被占用的锁时,JVM将记录锁的占有者,并且将请求计数置为1.如果同一个线程再次请求这个锁,计数将递增,每次占用线程退出同步块,计数器值将递减。直到0,锁将被释放。
下面的例子,如果内部锁不是重入,代码将死锁。
public class SynchronizedTest
{
public static void main(String[] args)
{
Stest s = new Stest();
for(int i=0; i<1000; i++)
{
new Thread(s).start();
}
}
}
class Stest implements Runnable{
public synchronized void call()
{
System.out.println(Thread.currentThread().getName()+"call invoked...");
compute();
}
public synchronized void compute()
{
System.out.println(Thread.currentThread().getName()+"compute invoked....");
}
@Override
public void run()
{
call();
}
}
结果:
没有出现死锁。
1. 对于每个可被多个线程访问的可变状态变量,如果所有访问它的线程在执行时都占有同一个锁,这种情况下,我们称这个变量是由这个锁保护的。
2.每个共享的可变变量都需要唯一一个确定的锁来保护。而维护者应该清楚这个锁。对于每一个涉及多个变量的不变约束,需要同一个锁保护其所有的变量。
活跃度与性能
1.通常简单性和性能之间是相互牵制的。实现一个同步策略时,不要过早地为了性能而牺牲简单性。
2.有些耗时的计算或操作,比如网络或控制台I/O,难以快速完成,执行这些操作期间不要占有锁。
参考:《JAVA并发编程实战》