前言
在当今多核处理器普及的时代,充分发挥多线程的优势成为了提升程序性能的关键。Java作为一门面向对象的编程语言,提供了丰富的并发编程工具和库,使得开发者能够轻松地构建高效、稳定的并发应用。然而,对于刚接触并发编程的开发者来说,面对众多的锁机制、并发工具和复杂的场景应用,可能会感到无从下手。本文将从基础到进阶,详细讲解Java中的各种锁机制、并发工具以及常见场景的实现方法,帮助读者全面理解并掌握Java并发编程的核心知识点。
一、Java并发编程基础
1. 线程与多线程
Java通过Thread类和Runnable接口提供了多线程的支持。多线程程序在运行时会将任务分解为多个线程,这些线程可以并发执行,从而提升程序的执行效率。示例:创建并启动线程
// 实现Runnable接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程执行中...");
}
}
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程
Thread thread = new Thread(new MyRunnable());
// 启动线程
thread.start();
}
}
2. 同步与互斥
在多线程环境下,多个线程可能同时访问共享资源,从而引发数据不一致或竞争条件。为了避免这些问题,Java提供了多种同步机制。
二、Java中的锁机制
1. 基础锁:synchronized关键字
synchronized关键字是Java中最基本的同步机制,可以修饰方法或代码块,确保同一时间只有一个线程可以执行被锁定的代码。示例:synchronized方法
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
示例:synchronized代码块
class Counter {
private int count = 0;
private Object lock = new Object();
public void increment() {
synchronized (lock) { // 使用锁对象
count++;
}
}
}
2. ReentrantLock:可重入锁
ReentrantLock是Java并发包java.util.concurrent.locks中的一个类,提供了比synchronized更灵活的锁机制。示例:ReentrantLock的基本用法
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock(); // 加锁
try {
count++;
} finally {
lock.unlock(); // 解锁
}
}
}
3. ReadWriteLock:读写锁
ReadWriteLock允许对资源进行读写分离,允许多个线程同时读取资源,但写入时需要独占锁。示例:ReadWriteLock的使用
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Counter {
private int count = 0;
private ReadWriteLock lock = new ReentrantReadWriteLock();
public void increment() {
lock.writeLock().lock(); // 写锁
try {
count++;
} finally {
lock.writeLock().unlock();
}
}
public int getCount() {
lock.readLock().lock(); // 读锁
try {
return count;
} finally {
lock.readLock().unlock();
}
}
}
三、Java中的并发工具
1. CountDownLatch:计数器闭锁
CountDownLatch用于等待一组线程完成任务后再继续执行后续操作。示例:CountDownLatch的使用
import java.util.concurrent.CountDownLatch;
class Task implements Runnable {
private CountDownLatch latch;
public Task(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
System.out.println("任务执行中...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务完成...");
latch.countDown(); // 通知闭锁
}
}
public class CountDownLatchDemo {
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(3); // 3个任务
for (int i = 0; i < 3; i++) {
new Thread(new Task(latch)).start();
}
try {
latch.await(); // 等待所有任务完成
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有任务完成,继续执行...");
}
}
2. Semaphore:信号量
Semaphore用于控制同时访问某一资源的线程数量。示例:Semaphore的使用
import java.util.concurrent.Semaphore;
class Task implements Runnable {
private Semaphore semaphore;
public Task(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire(); // 获取许可
System.out.println(Thread.currentThread().getName() + " 正在执行...");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " 执行完成...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // 释放许可
}
}
}
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2); // 最多允许2个线程同时执行
for (int i = 0; i < 5; i++) {
new Thread(new Task(semaphore), "线程" + i).start();
}
}
}
3. Executor框架:线程池
Java的Executor框架提供了一套接口和实现类,用于管理和复用线程,简化了线程池的使用。示例:线程池的使用
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3); // 固定大小的线程池
for (int i = 0; i < 5; i++) {
executor.execute(new Task());
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.MINUTES); // 等待所有任务完成
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Task implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 正在执行...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 执行完成...");
}
}
四、Java并发编程中的高级主题
1. CAS(Compare-And-Swap):无锁编程
CAS是一种无锁算法,通过比较内存中的值与预期值,如果相同则更新,否则重试。Java中的Atomic类族(如AtomicInteger)利用了CAS机制。示例:AtomicInteger的使用
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInt = new AtomicInteger(0);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
atomicInt.getAndIncrement(); // 使用CAS进行原子操作
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终值:" + atomicInt.get());
}
}
2. DCL(Double-Checked Locking):双重校验锁
DCL是一种优化单例模式的实现方式,在一定程度上避免了synchronized关键字的性能开销。示例:DCL的实现
class Singleton {
private volatile static Singleton instance;
private Singleton() {
// 防止反射攻击
if (instance != null) {
throw new RuntimeException("单例构造器被调用!");
}
}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
public class DCLEntry {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
}
}
五、常见场景实现
1. 银行转账场景
示例:使用ReentrantLock实现银行转账
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Account {
private double balance;
private Lock lock = new ReentrantLock();
public Account(double balance) {
this.balance = balance;
}
public void transfer(Account target, double amount) {
lock.lock();
try {
if (balance >= amount) {
balance -= amount;
target.balance += amount;
}
} finally {
lock.unlock();
}
}
public double getBalance() {
return balance;
}
}
public class BankTransfer {
public static void main(String[] args) {
Account alice = new Account(1000);
Account bob = new Account(0);
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
alice.transfer(bob, 1);
}
}).start();
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
bob.transfer(alice, 1);
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Alice的余额:" + alice.getBalance());
System.out.println("Bob的余额:" + bob.getBalance());
}
}
2. 售票系统场景
示例:使用Semaphore实现售票系统
import java.util.concurrent.Semaphore;
class TicketSeller implements Runnable {
private static int tickets = 100;
private Semaphore semaphore;
public TicketSeller(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
while (true) {
try {
semaphore.acquire(); // 获取许可
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + " 卖出一张票,剩余票数:" + (tickets--));
} else {
System.out.println("票已售罄!");
break;
}
semaphore.release(); // 释放许可
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TicketSystem {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5); // 允许5个线程同时售票
for (int i = 0; i < 10; i++) {
new Thread(new TicketSeller(semaphore), "售票员" + i).start();
}
}
}
六、总结
Java并发编程是提升程序性能和扩展性的关键,但同时也需要开发者具备扎实的理论基础和丰富的实践经验。本文详细介绍了Java中的锁机制、并发工具以及常见场景的实现方法,并通过代码示例帮助读者更好地理解和掌握相关知识点。希望本文能够为读者提供有价值的参考,助力他们在Java并发编程领域更进一步。