Java并发编程之锁
1. 引言
在多线程编程中,为了保证线程安全,我们经常会使用锁来控制共享资源的访问。Java提供了多种锁的实现方式,如synchronized关键字、ReentrantLock等。本文将介绍什么时候会用到锁,并通过代码示例来详细解释。
2. 什么是锁?
锁是多线程编程中用来控制对共享资源的访问的机制。当多个线程同时访问一个共享资源时,如果没有合适的锁机制,可能会导致数据的不一致或者竞争条件等问题。
锁机制可以分为两类:悲观锁和乐观锁。悲观锁认为并发访问共享资源的可能性很大,所以在访问共享资源之前先获取锁,确保不会出现并发问题。而乐观锁则认为并发访问的可能性很小,所以不会直接获取锁,而是在更新共享资源时进行检查,如果发现冲突,则进行重试或者放弃。
3. 什么时候会用到锁?
在以下场景中,我们通常会用到锁机制:
-
多个线程同时读写共享资源:当多个线程同时读写一个共享资源时,为了保证数据的一致性,可以使用锁来控制对共享资源的访问。下面是一个示例:
public class Counter { private volatile int count; public synchronized void increment() { count++; } public synchronized void decrement() { count--; } public int getCount() { return count; } } public class Main { public static void main(String[] args) throws InterruptedException { Counter counter = new Counter(); Thread incrementThread = new Thread(() -> { for (int i = 0; i < 100000; i++) { counter.increment(); } }); Thread decrementThread = new Thread(() -> { for (int i = 0; i < 100000; i++) { counter.decrement(); } }); incrementThread.start(); decrementThread.start(); incrementThread.join(); decrementThread.join(); System.out.println(counter.getCount()); } }
上述代码中,Counter类中的increment和decrement方法都使用了synchronized关键字来保证线程安全。通过创建两个线程分别对count进行增加和减少操作,最终输出的count值应为0。如果没有使用锁机制,可能会出现线程安全问题,导致count的值不正确。
-
需要实现互斥访问:有时候我们需要保证某个共享资源在同一时间只能被一个线程访问,这时可以使用锁来实现互斥访问。下面是一个示例:
public class PrintTask implements Runnable { private final Lock lock = new ReentrantLock(); private final String message; public PrintTask(String message) { this.message = message; } @Override public void run() { lock.lock(); // 获取锁 try { for (int i = 0; i < 10; i++) { System.out.println(message); } } finally { lock.unlock(); // 释放锁 } } } public class Main { public static void main(String[] args) { PrintTask task1 = new PrintTask("Hello"); PrintTask task2 = new PrintTask("World"); Thread thread1 = new Thread(task1); Thread thread2 = new Thread(task2); thread1.start(); thread2.start(); } }
上述代码中,PrintTask类使用了ReentrantLock来实现锁机制。通过创建两个线程分别执行不同的任务,由于每个任务都需要获取锁才能进行打印操作,所以实际上只有一个线程能够访问共享资源进行打印,从而实现了互斥访问。
-
需要实现等待-通知机制:有时候我们需要在某个条件满足时才能继续执行