死锁:
死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种相互等待的现象。若无外力干涉,它们都将无法推进下去。这种情况通常发生在多个线程都占有部分共享资源但又都在等待其它线程释放自己需要的资源时。
例如,在Java中,考虑以下场景:
public class DeadlockExample {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Acquired resource 1");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("Thread 1: Acquired resource 2");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Acquired resource 2");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource1) {
System.out.println("Thread 2: Acquired resource 1");
}
}
});
thread1.start();
thread2.start();
}
}
在这个例子中,thread1
和thread2
分别尝试获取对方已经持有的资源,导致两者都无法继续执行。因此,这个程序会陷入死锁状态。
Java中的volatile关键字:volatile
关键字用于修饰变量,它可以确保对变量的更新操作能被其他线程看到。当一个变量被声明为volatile
后,Java内存模型保证了对该变量的读写操作都是原子性的,且每次读取该变量时都会从主内存中读取最新值,而不是从工作内存(每个线程都有自己独立的工作内存)中读取。
volatile与synchronized的区别:
- 同步范围:
volatile
仅能用于修饰变量,而synchronized
可以作用于代码块、方法以及类。 - 内存语义:
volatile
仅保证了可见性,即一个线程修改了volatile变量的值后,其他线程可以立即看到该变化;而synchronized
除了保证可见性,还保证了原子性,即一次只有一个线程可以访问被锁定的对象或代码块。 - 执行顺序:
synchronized
可以确保指令重排序不会影响到多线程之间的数据一致性,而volatile
不能防止指令重排序。 - 性能开销:由于
synchronized
提供了更多的保障,所以它的性能开销比volatile
更大。
总的来说,volatile
适合用于简单的同步需求,比如确保某个标志位的改变能够及时地被其他线程看到。而对于复杂的并发控制场景,比如需要保证数据完整性的情况,应使用synchronized
或其他高级并发工具如Lock
。