Java Language Specification 和 How to make a dead lock using it 中 synchronized 中的 synchronized 是什么意思?
What does synchronized in synchronized mean from Java Language Specification and How to make a dead lock using it?
我正在阅读 Java 语言规范,其中有一个这样的例子:
class Test {
public static void main(String[] args) {
Test t = new Test();
synchronized(t) {
synchronized(t) {
System.out.println("made it!");
}
}
}
}
你可以从
http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.19
有一个注释
Note that this program would deadlock if a single thread were not permitted to lock a monitor more than once.
注释的确切含义是什么?如何使用它来制造死锁?
你不能那样死锁,因为你只有一个线程。
class DeadLock {
public synchronized void foo(int i) {
System.out.println("Processing :" + i );
if(i == 0) while(true){
try {
Thread.sleep(1000); //1000 milliseconds is one second.
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
};
System.out.println("Finished:" + i );
}
}
在你的主要
DeadLock dl = new DeadLock();
Integer[] intArray = {0, 1, 2, 3, 4, 5, 6, 7, 8 };
List<Integer> listOfIntegers =
new ArrayList<>(Arrays.asList(intArray));
listOfIntegers.stream().parallelStream().forEach(i -> dl.foo(i));
我没有测试过,但是当 foo(0) 启动时,它应该会阻止其他人启动。
示例表明synchronized是一个reentrant锁。
synchronized(t)
块只允许一个线程在其中,(以及程序中的任何其他 synchronized(t)
块)。
由于同步是可重入的,synchronized(t)
块中的线程可以进入另一个 synchronized(t)
块,尽管 synchronized(t)
块中已经有一个线程 - 该线程本身。
如果锁不是可重入的,第二个 synchronized(t)
会造成死锁,因为该线程在再次锁定之前必须放弃锁。
您不能使用此代码创建死锁。文本解释了为什么规则是这样的。规则是一个线程不能多次锁定同一个监视器。如果线程 可以 多次锁定同一个监视器,则会发生以下情况:
public static void main(String[] args) {
Test t = new Test();
synchronized(t) {
synchronized(t) {
System.out.println("made it!");
}
}
}
第一个 synchronized
会导致线程锁定 t
。当程序到达第二个 synchronized
时,线程将尝试锁定 t
,但是由于 t
已经被锁定(由线程本身),线程将永远坐在那里等待本身解锁 t
,但线程永远不会解锁 t
,因为必须等待自身解锁 t
,然后才能解锁 t
.
如果规则不同,事情就会是这样。但既然规则就是这样,就不会有僵局。
请注意,实际上没有人会编写这样的代码。但是用方法做同样的事情可能很常见。假设你有两个 public
方法,它们都需要锁定一些东西:
public class C {
SomeObject monitor;
public void method1() {
synchronized(monitor) {
...stuff...
}
}
public void method2() {
synchronized(monitor) {
...stuff
method1();
...stuff
}
}
}
如果不存在关于不锁定多次的规则,这将在 method2
调用 method1
时出现死锁。由于存在规则,因此在这种情况下您不必担心死锁。
Java 中的同步块是可重入的。这意味着,如果一个 Java 线程进入一个同步代码块,从而锁定该块同步的监视器对象,该线程可以进入同步的其他 Java 代码块监控对象。
public class Reentrant{
public synchronized outer(){
inner();
}
public synchronized inner(){
//do something
}
}
outer() 和 inner() 都声明为同步的,在 Java 中相当于一个 synchronized(this) 块。如果线程调用 outer(),则从 outer() 内部调用 inner() 没有问题,因为两个方法(或块)在同一个监视器对象 ("this") 上同步。如果线程已经持有监视器对象上的锁,则它可以访问在同一监视器对象上同步的所有块。这称为重入。线程可以重新进入它已经持有锁的任何代码块。
但是如果你有自己的自定义锁 class 那么你可以在同一监视器上的嵌套锁定中创建死锁,如果它不能重入的话。
public class Lock{
private boolean isLocked = false;
public synchronized void lock()
throws InterruptedException{
while(isLocked){
wait();
}
isLocked = true;
}
public synchronized void unlock(){
isLocked = false;
notify();
}
}
public class Reentrant2{
Lock lock = new Lock();
public outer(){
lock.lock();
inner();
lock.unlock();
}
public synchronized inner(){
lock.lock();
//do something
lock.unlock();
}
}
调用outer() 的线程将首先锁定Lock 实例。然后它将调用 inner()。在 inner() 方法中,线程将再次尝试锁定 Lock 实例。这将失败(意味着线程将被阻塞),因为 Lock 实例已经在 outer() 方法中被锁定。
为了使锁 class 可重入,我们需要做一个小改动:
public class Lock{
boolean isLocked = false;
Thread lockedBy = null;
int lockedCount = 0;
public synchronized void lock()
throws InterruptedException{
Thread callingThread = Thread.currentThread();
while(isLocked && lockedBy != callingThread){
wait();
}
isLocked = true;
lockedCount++;
lockedBy = callingThread;
}
public synchronized void unlock(){
if(Thread.curentThread() == this.lockedBy){
lockedCount--;
if(lockedCount == 0){
isLocked = false;
notify();
}
}
}
...
}
注意 while 循环(自旋锁)现在还考虑了锁定 Lock 实例的线程。如果锁被解锁(isLocked = false)或者调用线程是锁定Lock实例的线程,while循环将不会执行,调用lock()的线程将被允许退出方法。
另外,我们需要统计锁被同一个线程加锁的次数。否则,单次调用 unlock() 将解锁锁,即使锁已被多次锁定。我们不希望锁被解锁,直到锁定它的线程执行了与 lock() 调用相同数量的 unlock() 调用。
锁 class 现在是 ReentrantLock。
在 java.util.concurrent 包中你有 ReentrantLock 可用于重入。
检查此 link 阅读更多相关信息:http://tutorials.jenkov.com/java-concurrency/locks.html
我正在阅读 Java 语言规范,其中有一个这样的例子:
class Test {
public static void main(String[] args) {
Test t = new Test();
synchronized(t) {
synchronized(t) {
System.out.println("made it!");
}
}
}
}
你可以从 http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.19
有一个注释
Note that this program would deadlock if a single thread were not permitted to lock a monitor more than once.
注释的确切含义是什么?如何使用它来制造死锁?
你不能那样死锁,因为你只有一个线程。
class DeadLock {
public synchronized void foo(int i) {
System.out.println("Processing :" + i );
if(i == 0) while(true){
try {
Thread.sleep(1000); //1000 milliseconds is one second.
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
};
System.out.println("Finished:" + i );
}
}
在你的主要
DeadLock dl = new DeadLock();
Integer[] intArray = {0, 1, 2, 3, 4, 5, 6, 7, 8 };
List<Integer> listOfIntegers =
new ArrayList<>(Arrays.asList(intArray));
listOfIntegers.stream().parallelStream().forEach(i -> dl.foo(i));
我没有测试过,但是当 foo(0) 启动时,它应该会阻止其他人启动。
示例表明synchronized是一个reentrant锁。
synchronized(t)
块只允许一个线程在其中,(以及程序中的任何其他 synchronized(t)
块)。
由于同步是可重入的,synchronized(t)
块中的线程可以进入另一个 synchronized(t)
块,尽管 synchronized(t)
块中已经有一个线程 - 该线程本身。
如果锁不是可重入的,第二个 synchronized(t)
会造成死锁,因为该线程在再次锁定之前必须放弃锁。
您不能使用此代码创建死锁。文本解释了为什么规则是这样的。规则是一个线程不能多次锁定同一个监视器。如果线程 可以 多次锁定同一个监视器,则会发生以下情况:
public static void main(String[] args) {
Test t = new Test();
synchronized(t) {
synchronized(t) {
System.out.println("made it!");
}
}
}
第一个 synchronized
会导致线程锁定 t
。当程序到达第二个 synchronized
时,线程将尝试锁定 t
,但是由于 t
已经被锁定(由线程本身),线程将永远坐在那里等待本身解锁 t
,但线程永远不会解锁 t
,因为必须等待自身解锁 t
,然后才能解锁 t
.
如果规则不同,事情就会是这样。但既然规则就是这样,就不会有僵局。
请注意,实际上没有人会编写这样的代码。但是用方法做同样的事情可能很常见。假设你有两个 public
方法,它们都需要锁定一些东西:
public class C {
SomeObject monitor;
public void method1() {
synchronized(monitor) {
...stuff...
}
}
public void method2() {
synchronized(monitor) {
...stuff
method1();
...stuff
}
}
}
如果不存在关于不锁定多次的规则,这将在 method2
调用 method1
时出现死锁。由于存在规则,因此在这种情况下您不必担心死锁。
Java 中的同步块是可重入的。这意味着,如果一个 Java 线程进入一个同步代码块,从而锁定该块同步的监视器对象,该线程可以进入同步的其他 Java 代码块监控对象。
public class Reentrant{
public synchronized outer(){
inner();
}
public synchronized inner(){
//do something
}
}
outer() 和 inner() 都声明为同步的,在 Java 中相当于一个 synchronized(this) 块。如果线程调用 outer(),则从 outer() 内部调用 inner() 没有问题,因为两个方法(或块)在同一个监视器对象 ("this") 上同步。如果线程已经持有监视器对象上的锁,则它可以访问在同一监视器对象上同步的所有块。这称为重入。线程可以重新进入它已经持有锁的任何代码块。
但是如果你有自己的自定义锁 class 那么你可以在同一监视器上的嵌套锁定中创建死锁,如果它不能重入的话。
public class Lock{
private boolean isLocked = false;
public synchronized void lock()
throws InterruptedException{
while(isLocked){
wait();
}
isLocked = true;
}
public synchronized void unlock(){
isLocked = false;
notify();
}
}
public class Reentrant2{
Lock lock = new Lock();
public outer(){
lock.lock();
inner();
lock.unlock();
}
public synchronized inner(){
lock.lock();
//do something
lock.unlock();
}
}
调用outer() 的线程将首先锁定Lock 实例。然后它将调用 inner()。在 inner() 方法中,线程将再次尝试锁定 Lock 实例。这将失败(意味着线程将被阻塞),因为 Lock 实例已经在 outer() 方法中被锁定。
为了使锁 class 可重入,我们需要做一个小改动:
public class Lock{
boolean isLocked = false;
Thread lockedBy = null;
int lockedCount = 0;
public synchronized void lock()
throws InterruptedException{
Thread callingThread = Thread.currentThread();
while(isLocked && lockedBy != callingThread){
wait();
}
isLocked = true;
lockedCount++;
lockedBy = callingThread;
}
public synchronized void unlock(){
if(Thread.curentThread() == this.lockedBy){
lockedCount--;
if(lockedCount == 0){
isLocked = false;
notify();
}
}
}
...
}
注意 while 循环(自旋锁)现在还考虑了锁定 Lock 实例的线程。如果锁被解锁(isLocked = false)或者调用线程是锁定Lock实例的线程,while循环将不会执行,调用lock()的线程将被允许退出方法。
另外,我们需要统计锁被同一个线程加锁的次数。否则,单次调用 unlock() 将解锁锁,即使锁已被多次锁定。我们不希望锁被解锁,直到锁定它的线程执行了与 lock() 调用相同数量的 unlock() 调用。
锁 class 现在是 ReentrantLock。
在 java.util.concurrent 包中你有 ReentrantLock 可用于重入。
检查此 link 阅读更多相关信息:http://tutorials.jenkov.com/java-concurrency/locks.html