Java线程死锁发生的常见行为是双方互相持有对方需要的锁:即两个或多个线程在等待彼此持有的锁,导致所有线程都无法继续执行下去。这种情况通常会产生一个循环等待的场景。
举例代码:
public class DeadlockExample {
private static Object lockA = new Object();
private static Object lockB = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockA) {
System.out.println("Thread 1 acquired lock A");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println("Thread 1 acquired lock B");
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockB) {
System.out.println("Thread 2 acquired lock B");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockA) {
System.out.println("Thread 2 acquired lock A");
}
}
}
});
thread1.start();
thread2.start();
}
}
为避免死锁,可以采取以下措施:
使用ReentrantLock
使用ReentrantLock时,需要将所有涉及到共享资源的代码放在lock()和unlock()方法之间,才能保证线程安全。在之前的代码示例中,if代码块没有被包括在lock()和unlock()方法之间,因此不具备线程安全性。
以下是修改后的示例代码:
public class DeadlockExample {
private static final ReentrantLock lockA = new ReentrantLock();
private static final ReentrantLock lockB = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
lockA.lock();
System.out.println("Thread 1 acquired lock A");
Thread.sleep(100);
if (lockB.tryLock(100, TimeUnit.MILLISECONDS)) {
System.out.println("Thread 1 acquired lock B");
Thread.sleep(100);
} else {
System.out.println("Thread 1 failed to acquire lock B, aborting");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lockB.isHeldByCurrentThread()) {
lockB.unlock();
System.out.println("Thread 1 released lock B");
}
lockA.unlock();
System.out.println("Thread 1 released lock A");
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
lockB.lock();
System.out.println("Thread 2 acquired lock B");
Thread.sleep(100);
if (lockA.tryLock(100, TimeUnit.MILLISECONDS)) {
System.out.println("Thread 2 acquired lock A");
Thread.sleep(100);
} else {
System.out.println("Thread 2 failed to acquire lock A, aborting");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lockA.isHeldByCurrentThread()) {
lockA.unlock();
System.out.println("Thread 2 released lock A");
}
lockB.unlock();
System.out.println("Thread 2 released lock B");
}
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
以上代码中,在每个线程的run方法中,我们依次获取两把锁,并在获取到锁之后进行相应的操作。在释放锁时,需要先判断当前线程是否持有该锁,如果是,则释放该锁,并打印相应的信息。
这样修改后,就能保证线程安全和避免死锁的问题了。
使用Semaphore
以下是修改后的示例代码:
public class DeadlockExample {
private static final Semaphore semaphoreA = new Semaphore(1);
private static final Semaphore semaphoreB = new Semaphore(1);
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphoreA.acquire();
System.out.println("Thread 1 acquired semaphore A");
Thread.sleep(100);
while (!semaphoreB.tryAcquire()) {
semaphoreA.release();
System.out.println("Thread 1 released semaphore A and waiting for semaphore B");
semaphoreA.acquire();
System.out.println("Thread 1 acquired semaphore A again");
}
System.out.println("Thread 1 acquired semaphore B");
Thread.sleep(100);
semaphoreB.release();
System.out.println("Thread 1 released semaphore B");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphoreA.release();
System.out.println("Thread 1 released semaphore A");
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphoreB.acquire();
System.out.println("Thread 2 acquired semaphore B");
Thread.sleep(100);
while (!semaphoreA.tryAcquire()) {
semaphoreB.release();
System.out.println("Thread 2 released semaphore B and waiting for semaphore A");
semaphoreB.acquire();
System.out.println("Thread 2 acquired semaphore B again");
}
System.out.println("Thread 2 acquired semaphore A");
Thread.sleep(100);
semaphoreA.release();
System.out.println("Thread 2 released semaphore A");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphoreB.release();
System.out.println("Thread 2 released semaphore B");
}
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
以上代码中,如果全部使用acquire(),那么在当前线程获取到第一个资源后,如果无法获取第二个资源,就会释放第一个资源,并进入等待状态。然而,由于此时其他线程可能已经获取了第一个资源并在等待第二个资源,因此当前线程可能永远无法获取到第二个资源,从而导致死锁的发生。
为了避免这种情况,可以使用tryAcquire()方法来尝试获取第二个资源,如果无法获取,则先释放第一个资源再进行等待。
这样修改后,就能保证线程安全和避免死锁的问题了。
Java中线程饥饿(Thread Starvation)是指某个或某些线程无法获得所需的资源,从而无法继续执行下去,导致程序出现假死等异常情况。
举个例子,如果一个高优先级的线程一直占用某个共享资源,并且低优先级的线程总是无法获取该资源,那么这些低优先级的线程就会一直处于等待状态,无法执行下去,从而导致线程饥饿的发生。
另外,线程饥饿也可能发生在多个线程竞争同一资源时,如果某些线程总是能够比其他线程更快地获取到该资源,那么其他线程就会一直处于等待状态,无法及时完成任务,从而导致线程饥饿的发生。
为避免线程饥饿的发生,可以采取以下措施:
总之,在编写多线程程序时需要注意线程饥饿问题,并采取相应措施来保证程序的正常执行。
以下是一个简单的代码示例,模拟了线程饥饿的情况。
public class ThreadStarvationExample {
private static final Object lock = new Object();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
final int index = i;
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println("Thread " + index + " acquired lock");
try {
// 模拟某些线程需要占用锁较长时间
if (index == 2 || index == 3) {
Thread.sleep(5000);
} else {
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + index + " released lock");
}
}
}).start();
}
}
}
以上代码中,我们创建了10个线程,并对它们共享的一个锁进行竞争。其中,第2个线程和第3个线程分别需要占用锁5秒钟和其他线程相比更久的时间。
如果运行以上代码,我们可能会发现第2个线程和第3个线程总是优先获得锁,而其他线程则会等待较长时间才能获取到锁,从而导致这些线程在整个程序执行期间都无法正常执行下去,出现线程饥饿的情况。
为避免线程饥饿的发生,我们可以对占用锁时间较长的线程做出调整,例如将它们设置为低优先级或者减少其持有锁的时间等措施。
另外,我们还可以使用公平锁来保证资源的公平分配,避免某个线程长期占用某个资源。以下是修改后的代码示例:
public class ThreadStarvationExample {
private static final ReentrantLock lock = new ReentrantLock(true);
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
final int index = i;
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println("Thread " + index + " acquired lock");
try {
if (index == 2 || index == 3) {
// 将占用锁时间较长的线程设置为低优先级
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
Thread.sleep(5000);
} else {
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 减少持有锁的时间,增加其他线程获取锁的机会
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + index + " released lock");
}
}
}
}).start();
}
}
}