本文介绍了Java高并发入门的相关知识,包括线程和进程的基本概念、Java线程模型、并发编程的基本概念以及线程的创建和管理。文章还详细讲解了线程同步与互斥、并发工具类的应用,以及如何避免并发编程中的常见问题。
1. Java并发编程基础理解线程与进程
在计算机系统中,进程和线程是两个核心概念,它们共同构成了操作系统资源管理和并发执行的基础。
进程是指一个程序的执行实例,它是操作系统中的基本单位。每个进程拥有独立的地址空间,包含代码段、数据段、堆、栈等。
线程是进程内的一个执行单元,共享进程的资源,但有自己的栈和局部变量。一个进程中可以有多个线程并发运行,以达到更好的性能和资源利用率。
Java线程模型介绍
Java中的线程模型基于操作系统的线程模型。Java虚拟机(JVM)是线程的运行环境,而每个线程对应着一个运行中的Java方法。Java线程模型可以分为几个层次:
- 操作系统层:操作系统提供线程调度和资源管理功能。
- JVM层:Java虚拟机负责线程的创建和销毁。
- Java API层:Java提供了丰富的API来管理线程,如
Thread
类和Runnable
接口。
下面是一个简单的Java线程示例,使用Thread
类创建并启动一个线程。
public class SimpleThreadExample {
public static void main(String[] args) {
// 创建一个新的线程
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("线程正在运行...");
}
};
// 启动线程
thread.start();
}
}
Java并发编程的基本概念
Java并发编程涉及多个线程同时访问共享资源,为了保证程序的正确性,需要了解一些基本概念:
- 线程安全:线程安全是指在多个线程访问同一个资源时,能够保证资源的使用不会出错。
- 锁定:锁定机制是用来确保在任意时刻只有一个线程能访问共享资源。
- 死锁:两个或多个线程互相等待对方释放资源,导致所有线程都无法继续执行。
- 竞态条件:多个线程同时修改同一资源,导致结果不确定。
- 线程同步:确保多个线程在访问共享资源时,按照预定的顺序执行。
使用Thread类创建线程
使用Thread
类是最直接的创建线程的方式。通过继承Thread
类并重写run
方法,可以创建一个新线程。
public class ThreadExample extends Thread {
public void run() {
System.out.println("我在一个新的线程中运行");
}
public static void main(String[] args) {
ThreadExample example = new ThreadExample();
example.start();
}
}
使用Runnable和Callable接口
Runnable
接口和Callable
接口提供了另一种创建线程的方法。它们不像Thread
类那样直接表示一个线程,而是提供一个线程执行的方法定义。
public class RunnableExample implements Runnable {
public void run() {
System.out.println("我在一个Runnable线程中运行");
}
public static void main(String[] args) {
RunnableExample example = new RunnableExample();
Thread thread = new Thread(example);
thread.start();
}
}
public class CallableExample implements Callable<String> {
@Override
public String call() throws Exception {
return "我在一个Callable线程中运行";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableExample example = new CallableExample();
FutureTask<String> futureTask = new FutureTask<>(example);
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get();
System.out.println(result);
}
}
线程的生命周期和状态
线程在生命周期中会经历几个状态:
- New:线程已经被创建,但还没有调用
start
方法。 - Runnable:线程已经被创建,并且正在执行
run
方法。 - Blocked:等待获取锁。
- Waiting:等待其他线程的特定动作。
- Timed-Waiting:等待指定时间后唤醒。
- Terminated:线程执行完毕。
下面是一个具体的Java代码示例,展示如何创建和管理线程状态。
public class ThreadLifeCycle {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
System.out.println("线程状态: " + thread.getState()); // 输出:NEW
thread.start();
Thread.sleep(500); // 暂停主线程
System.out.println("线程状态: " + thread.getState()); // 输出:RUNNABLE
thread.interrupt(); // 打断线程
Thread.sleep(1000); // 暂停主线程
System.out.println("线程状态: " + thread.getState()); // 输出:TERMINATED
}
}
3. 线程同步与互斥
synchronized关键字详解
synchronized
关键字用于创建同步锁,保证同一时间只有一个线程能够访问共享资源。
public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终计数: " + example.getCount()); // 输出:2000
}
}
互斥锁与读写锁的应用
互斥锁通常通过synchronized
关键字实现,而读写锁则提供了更细粒度的控制,适用于读多写少的场景。
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private final ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock rlock = rwlock.readLock();
private final ReentrantReadWriteLock.WriteLock wlock = rwlock.writeLock();
private int count = 0;
public void read() {
rlock.lock();
try {
System.out.println("读取计数: " + count);
} finally {
rlock.unlock();
}
}
public void write() {
wlock.lock();
try {
count++;
System.out.println("写入计数: " + count);
} finally {
wlock.unlock();
}
}
public static void main(String[] args) {
ReadWriteLockExample example = new ReadWriteLockExample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.read();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.write();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
volatile关键字的作用
volatile
关键字保证了变量的可见性和有序性,但不保证原子性。
public class VolatileExample {
private volatile int count = 0;
public void increment() {
count++;
}
public static void main(String[] args) {
VolatileExample example = new VolatileExample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终计数: " + example.count); // 输出:2000
}
}
4. 并发工具类介绍
CountDownLatch和CyclicBarrier
CountDownLatch
和CyclicBarrier
都是用于线程同步的工具类,前者用于等待全部线程完成,后者用于等待所有线程到达某个屏障点。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
public class SyncToolsExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
Thread thread1 = new Thread(() -> {
System.out.println("线程1开始");
try {
Thread.sleep(1000);
System.out.println("线程1结束");
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
System.out.println("线程2开始");
try {
cyclicBarrier.await();
System.out.println("所有线程都到达屏障点");
} catch (Exception e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
countDownLatch.await(); // 等待线程1完成
try {
cyclicBarrier.await(); // 等待线程2到达屏障点
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("所有线程已完成");
}
}
Semaphore和Exchanger
Semaphore
用于控制同时访问特定资源的线程数量,而Exchanger
用于两个线程之间的数据交换。
import java.util.concurrent.Semaphore;
import java.util.concurrent.Exchanger;
public class SemaphoreExchangerExample {
private static final Semaphore semaphore = new Semaphore(2);
private static final Exchanger<String> exchanger = new Exchanger<>();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
try {
semaphore.acquire();
System.out.println("线程1获取到许可");
String result = exchanger.exchange("Hello");
System.out.println("线程1交换结果: " + result);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
semaphore.acquire();
System.out.println("线程2获取到许可");
String result = exchanger.exchange("World");
System.out.println("线程2交换结果: " + result);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
并发集合类(ConcurrentHashMap, CopyOnWriteArrayList等)
ConcurrentHashMap
和CopyOnWriteArrayList
是并发集合类,提供了线程安全的集合操作。
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
public class ConcurrentCollectionsExample {
public static void main(String[] args) {
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
Thread thread1 = new Thread(() -> {
map.put("key1", "value1");
list.add("value1");
System.out.println("线程1添加完成");
});
Thread thread2 = new Thread(() -> {
map.put("key2", "value2");
list.add("value2");
System.out.println("线程2添加完成");
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("map: " + map);
System.out.println("list: " + list);
}
}
5. 并发编程中的常见问题
死锁的现象及避免方法
死锁是一种严重的并发问题,可以通过设计合理的锁顺序来避免。
public class DeadlockExample {
public static void main(String[] args) {
Resource r1 = new Resource();
Resource r2 = new Resource();
Thread thread1 = new Thread(() -> {
synchronized (r1) {
synchronized (r2) {
System.out.println("线程1获取两个锁");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (r2) {
synchronized (r1) {
System.out.println("线程2获取两个锁");
}
}
});
thread1.start();
thread2.start();
}
}
class Resource {
// 资源类
}
活锁与饥饿
活锁是指一个线程不断尝试执行任务但无法完成,而饥饿是指某些线程长时间无法获得执行机会。
下面是一个具体的Java代码示例,展示如何避免活锁和饥饿。
public class StarvationExample {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (true) {
synchronized (StarvationExample.class) {
System.out.println("线程1运行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (StarvationExample.class) {
System.out.println("线程2运行");
}
});
thread1.start();
thread2.start();
}
}
避免竞态条件的方法
竞态条件是指多个线程同时修改共享资源导致结果不确定的问题。可以通过使用synchronized
关键字或并发集合类来避免。
public class RaceConditionExample {
private static int count = 0;
public static synchronized void increment() {
count++;
}
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
increment();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终计数: " + count); // 输出:2000
}
}
6. 实战案例:简单的银行账户转账项目
使用多线程模拟多个用户同时进行转账操作
下面是一个简单的银行账户转账项目,模拟多个用户同时进行转账操作,并保证账户余额的正确性。
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class BankAccount {
private static final ConcurrentHashMap<Integer, BankAccount> accounts = new ConcurrentHashMap<>();
private final int id;
private AtomicInteger balance;
public BankAccount(int id, int initialBalance) {
this.id = id;
this.balance = new AtomicInteger(initialBalance);
accounts.put(id, this);
}
public void deposit(int amount) {
balance.addAndGet(amount);
}
public void withdraw(int amount) {
balance.addAndGet(-amount);
}
public int getBalance() {
return balance.get();
}
public static void main(String[] args) {
BankAccount account1 = new BankAccount(1, 1000);
BankAccount account2 = new BankAccount(2, 2000);
Thread thread1 = new Thread(() -> {
account1.withdraw(500);
account2.deposit(500);
System.out.println("账户1余额: " + account1.getBalance());
System.out.println("账户2余额: " + account2.getBalance());
});
Thread thread2 = new Thread(() -> {
account2.withdraw(500);
account1.deposit(500);
System.out.println("账户1余额: " + account1.getBalance());
System.out.println("账户2余额: " + account2.getBalance());
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("账户1最终余额: " + account1.getBalance());
System.out.println("账户2最终余额: " + account2.getBalance());
}
}
保证账户余额正确性的方法
为了确保账户余额的正确性,可以使用AtomicInteger
等原子类,或者使用synchronized
关键字或并发集合类来保证线程安全。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BankAccountWithLock {
private final int id;
private int balance;
private final Lock lock = new ReentrantLock();
public BankAccountWithLock(int id, int initialBalance) {
this.id = id;
this.balance = initialBalance;
}
public void deposit(int amount) {
lock.lock();
try {
balance += amount;
} finally {
lock.unlock();
}
}
public void withdraw(int amount) {
lock.lock();
try {
balance -= amount;
} finally {
lock.unlock();
}
}
public int getBalance() {
return balance;
}
public static void main(String[] args) {
BankAccountWithLock account1 = new BankAccountWithLock(1, 1000);
BankAccountWithLock account2 = new BankAccountWithLock(2, 2000);
Thread thread1 = new Thread(() -> {
account1.withdraw(500);
account2.deposit(500);
System.out.println("账户1余额: " + account1.getBalance());
System.out.println("账户2余额: " + account2.getBalance());
});
Thread thread2 = new Thread(() -> {
account2.withdraw(500);
account1.deposit(500);
System.out.println("账户1余额: " + account1.getBalance());
System.out.println("账户2余额: " + account2.getBalance());
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("账户1最终余额: " + account1.getBalance());
System.out.println("账户2最终余额: " + account2.getBalance());
}
}
共同学习,写下你的评论
评论加载中...
作者其他优质文章