Java 信号量导致多线程死锁
Java Semaphore cause deadlock on multithread
使用 Semaphore 时需要注意多线程问题吗?
经过我的测试似乎有一段时间 Semaphore#release 不会导致获取唤醒,即使有足够的许可。
下面是我的测试代码。
- 有 2 个许可的信号量
- 线程 3 和线程 2 先启动
- thread-3 获得许可,等待
lock
thread-1 通知
- thread-2 获得许可,等待
lock1
thread-3 通知
- 线程1启动,线程1休眠30ms,线程2先启动
- 线程 1 通知
lock
,获得 2 个许可
- thread-3 唤醒,通知
lock1
,sleep(1)
sleep 1 ms for thread-2 first,release许可证
- thread-2 唤醒,获得一个许可,然后释放一个许可并释放另一个许可
随机迭代会造成死锁,这样输出一些日志。
in 3, a = 2
in 2 ,a = 2
in in 2 lock 1, a = 0
in 1 , a = 0
acquire and release 3
in in 2 locked, a = 0
out 3 ,a = 0
in 1 locked, a = 0
acquire and release 2
out 2
out 1 ,a = 2
-------------------------------------------------------------- 0
in 2 ,a = 2
in 3, a = 1
in 1 , a = 0
in in 2 lock 1, a = 0
acquire and release 3
out 3 ,a = 1
//deadlock here
thread-3 Semaphore release permit后,不会引起thread-2唤醒,然后thread-1和thread-3永远等待acquire
下面是我的测试代码
import java.util.concurrent.Semaphore;
/**
* Created by rqg on 6/10/17.
*/
public class WaitTest {
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(2);
final Object lock = new Object();
final Object lock1 = new Object();
// testSemaphore(semaphore, lock, lock1);
for (int i = 0; i < 10000; i++) {
testSemaphore(semaphore, lock, lock1);
System.out.println("--------------------------------------------------------------------------------- " + i);
}
}
private static void testSemaphore(Semaphore semaphore, Object lock, Object lock1) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
try {
Thread.sleep(30);
synchronized (lock) {
lock.notify();
}
System.out.println("in 1 , a = " + semaphore.availablePermits());
semaphore.acquire(2);
System.out.println("in 1 locked, a = " + semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
semaphore.release(2);
System.out.println("out 1 ,a = " + semaphore.availablePermits());
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
System.out.println("in 2 ,a = " + semaphore.availablePermits());
semaphore.acquire();
synchronized (lock1) {
lock1.wait();
}
System.out.println("in in 2 lock 1, a = " + semaphore.availablePermits());
semaphore.acquire();
System.out.println("in in 2 locked, a = " + semaphore.availablePermits());
semaphore.release();
semaphore.release();
System.out.println("acquire and release 2");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("out 2");
}
};
Thread t3 = new Thread() {
@Override
public void run() {
try {
System.out.println("in 3, a = " + semaphore.availablePermits());
semaphore.acquire();
synchronized (lock) {
lock.wait();
}
synchronized (lock1) {
lock1.notify();
}
sleep(1);
semaphore.release();
System.out.println("acquire and release 3");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("out 3 ,a = " + semaphore.availablePermits());
}
};
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
}
}
这是发生死锁时我的胎面转储
信号量只保留许可数。来自信号量文档:
Conceptually, a semaphore maintains a set of permits. Each {@link
acquire} blocks if necessary until a permit is available, and then takes it. Each {@link #release} adds a permit, potentially
releasing a blocking acquirer. However, no actual permit objects
are used; the {@code Semaphore} just keeps a count of the number
available and acts accordingly.
我的意思是程序应该注意同步。
Semaphores are often used to restrict the number of threads than can
access some (physical or logical) resource
你认为死锁发生是因为释放没有发出停放线程的信号。
我发现了一些为此报告的错误
http://bugs.java.com/view_bug.do?bug_id=7011859
java.util.concurrent.locks.AbstractQueuedSynchronizer
被 Semaphore
用来做一些同步工作。 它有 FIFO 属性,这会导致问题。
我有2个许可,thread-3释放一个许可后,只有一个许可可用,根据Semaphore
FIFO获取顺序,如果我的thread-3 acquire(2)
发生在[=15=之前], 线程 3 将永远阻塞。
我将 Semaphore 与 fair=false
一起使用,内部将初始化 NonfairSync
其中 extends AbstractQueuedSynchronizer
.
下面的代码导致信号量总是流 FIFO 获取顺序。
/**
*java.util.concurrent.locks.AbstractQueuedSynchronizer#doAcquireSharedInterruptibly
*/
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
使用 Semaphore 时需要注意多线程问题吗? 经过我的测试似乎有一段时间 Semaphore#release 不会导致获取唤醒,即使有足够的许可。
下面是我的测试代码。
- 有 2 个许可的信号量
- 线程 3 和线程 2 先启动
- thread-3 获得许可,等待
lock
thread-1 通知
- thread-2 获得许可,等待
lock1
thread-3 通知
- 线程1启动,线程1休眠30ms,线程2先启动
- 线程 1 通知
lock
,获得 2 个许可 - thread-3 唤醒,通知
lock1
,sleep(1)
sleep 1 ms for thread-2 first,release许可证 - thread-2 唤醒,获得一个许可,然后释放一个许可并释放另一个许可
随机迭代会造成死锁,这样输出一些日志。
in 3, a = 2
in 2 ,a = 2
in in 2 lock 1, a = 0
in 1 , a = 0
acquire and release 3
in in 2 locked, a = 0
out 3 ,a = 0
in 1 locked, a = 0
acquire and release 2
out 2
out 1 ,a = 2
-------------------------------------------------------------- 0
in 2 ,a = 2
in 3, a = 1
in 1 , a = 0
in in 2 lock 1, a = 0
acquire and release 3
out 3 ,a = 1
//deadlock here
thread-3 Semaphore release permit后,不会引起thread-2唤醒,然后thread-1和thread-3永远等待acquire
下面是我的测试代码
import java.util.concurrent.Semaphore;
/**
* Created by rqg on 6/10/17.
*/
public class WaitTest {
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(2);
final Object lock = new Object();
final Object lock1 = new Object();
// testSemaphore(semaphore, lock, lock1);
for (int i = 0; i < 10000; i++) {
testSemaphore(semaphore, lock, lock1);
System.out.println("--------------------------------------------------------------------------------- " + i);
}
}
private static void testSemaphore(Semaphore semaphore, Object lock, Object lock1) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
try {
Thread.sleep(30);
synchronized (lock) {
lock.notify();
}
System.out.println("in 1 , a = " + semaphore.availablePermits());
semaphore.acquire(2);
System.out.println("in 1 locked, a = " + semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
semaphore.release(2);
System.out.println("out 1 ,a = " + semaphore.availablePermits());
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
System.out.println("in 2 ,a = " + semaphore.availablePermits());
semaphore.acquire();
synchronized (lock1) {
lock1.wait();
}
System.out.println("in in 2 lock 1, a = " + semaphore.availablePermits());
semaphore.acquire();
System.out.println("in in 2 locked, a = " + semaphore.availablePermits());
semaphore.release();
semaphore.release();
System.out.println("acquire and release 2");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("out 2");
}
};
Thread t3 = new Thread() {
@Override
public void run() {
try {
System.out.println("in 3, a = " + semaphore.availablePermits());
semaphore.acquire();
synchronized (lock) {
lock.wait();
}
synchronized (lock1) {
lock1.notify();
}
sleep(1);
semaphore.release();
System.out.println("acquire and release 3");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("out 3 ,a = " + semaphore.availablePermits());
}
};
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
}
}
这是发生死锁时我的胎面转储
信号量只保留许可数。来自信号量文档:
Conceptually, a semaphore maintains a set of permits. Each {@link acquire} blocks if necessary until a permit is available, and then takes it. Each {@link #release} adds a permit, potentially releasing a blocking acquirer. However, no actual permit objects are used; the {@code Semaphore} just keeps a count of the number available and acts accordingly.
我的意思是程序应该注意同步。
Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource
你认为死锁发生是因为释放没有发出停放线程的信号。 我发现了一些为此报告的错误 http://bugs.java.com/view_bug.do?bug_id=7011859
java.util.concurrent.locks.AbstractQueuedSynchronizer
被 Semaphore
用来做一些同步工作。 它有 FIFO 属性,这会导致问题。
我有2个许可,thread-3释放一个许可后,只有一个许可可用,根据Semaphore
FIFO获取顺序,如果我的thread-3 acquire(2)
发生在[=15=之前], 线程 3 将永远阻塞。
我将 Semaphore 与 fair=false
一起使用,内部将初始化 NonfairSync
其中 extends AbstractQueuedSynchronizer
.
下面的代码导致信号量总是流 FIFO 获取顺序。
/**
*java.util.concurrent.locks.AbstractQueuedSynchronizer#doAcquireSharedInterruptibly
*/
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}