在 Java 中,可以通过多种方式实现并发编程,如使用线程、线程池、锁等。
线程
创建线程
在Java中,启动一个main函数就代表启动了一个JVM进程,main函数所在的线程是这个进程的主线程,该JVM进程中的所有线程共享该JVM的堆和方法区,同时JVM在每个线程创建时为其分配各自的PC、虚拟机栈和本地方法栈,每个线程有一个Thread对象维护其上下文。
Java 中有两种方式可以创建线程:继承 Thread 类和实现 Runnable 接口。
继承 Thread 类
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running");
}
}
实现 Runnable 接口
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread running");
}
}
启动线程
启动线程有两种方式:调用 start() 方法或直接调用 run() 方法。
调用 start() 方法
MyThread myThread = new MyThread();
myThread.start();
直接调用 run() 方法
MyRunnable myRunnable = new MyRunnable();
myRunnable.run();
线程状态及转换
Java 中的线程状态有新建、可运行、运行、阻塞、等待、定时等待、中断、终止等。线程状态可以通过 Thread 类中的getState() 方法获取。线程状态的转换可以通过 Thread 类中的 interrupt()、 sleep()、 yield() 等方法实现。
线程池
线程池是一种可复用的线程集合,可以统一管理和调度线程,减少系统开销。 Java 中的 Executor 和 ExecutorService 是线程池的核心接口。
创建线程池
Java 中提供了多种线程池的实现,如 newCachedThreadPool、 newFixedThreadPool、 newSingleThreadExecutor、 newScheduledThreadPool 等。
newCachedThreadPool
该线程池具有缓冲功能,可以缓存已创建的线程,当有新的任务来时,可以直接使用缓存的线程,避免了重新创建线程的开销。但是,如果任务量一直很大,缓存的线程会越来越多,可能会造成内存溢出。
newFixedThreadPool
该线程池创建固定数量的线程,当有新的任务来时,会在这些线程中轮询执行。该线程池适用于任务的执行时间较短的情况。如果任务的执行时间较长,可能会导致某些线程一直处于空闲状态。
newSingleThreadExecutor
该线程池只有一个线程在工作,当任务来时,会在该线程中执行。如果该线程在执行任务时出现异常,会尝试重新执行该任务。该线程池适用于任务的执行时间较短且需要保证顺序执行的情况。如果任务的执行时间较长,可能会出现任务等待时间过长的情况。
newScheduledThreadPool
该线程池可以定时执行任务或按照一定的时间间隔执行任务。可以通过 ThreadPoolExecutor 的 scheduleAtFixedRate() 或 scheduleWithFixedDelay() 方法实现定时执行任务。
线程池的使用
使用线程池可以避免创建过多的线程,降低系统开销,提高程序的响应速度。以下是使用线程池的步骤:
- 创建线程池对象:通过实现 Executor 接口或继承 ThreadPoolExecutor 类来创建线程池对象。
- 提交任务:通过调用线程池的 execute() 或 submit() 方法来提交任务。
- 处理任务结果:如果任务实现了 Callable 接口,可以通过 Future 对象获取任务的执行结果。
- 关闭线程池:任务执行完成后,需要关闭线程池,释放资源。
下面是一个使用线程池的示例代码:
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executor.execute(new MyRunnable(i));
}
executor.shutdown();
锁
在 Java 中,可以使用 synchronized 关键字或 Lock 接口来实现锁的功能。
synchronized 关键字
synchronized 关键字可以用来修饰方法或代码块,实现同步访问共享资源的功能。当一个线程进入 synchronized 修饰的代码块或方法时,会获取一个锁,其他线程需要等待该锁释放后才能进入。
public synchronized void syncMethod() {
// synchronized code block
}
Lock 接口
Lock 接口提供了更灵活的锁功能,可以支持公平锁和非公平锁、可重入锁等。Lock 接口的实现类有 ReentrantLock、 ReadWriteLock 等。
下面是一个使用 ReentrantLock 的示例代码:
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// critical section protected by lock
} finally {
lock.unlock();
}
其他并发工具类
CountDownLatch 和 CyclicBarrier
CountDownLatch 和 CyclicBarrier 是 Java 中的计时器工具类,可以实现等待某个条件成立的功能。CountDownLatch 可以等待某个计数器归零后执行后续操作,CyclicBarrier 可以等待多个线程都到达某个屏障点后执行后续操作。
ConcurrentHashMap 和 ConcurrentLinkedQueue
ConcurrentHashMap 和 ConcurrentLinkedQueue 是 Java 中的并发集合类,支持多线程并发访问。这些集合类采用了分段锁或无锁机制,提高了并发访问的性能和可靠性。
Java 中的并发编程技术有很多种,在实际应用中,需要根据具体的业务场景选择合适的并发处理方式,提高程序的性能和可靠性。