1、简述
随着计算机硬件的发展,多核处理器的普及和内存容量的增加,利用多线程实现异步并发成为提升程序性能的重要途径。在Java中,多线程的使用能够更好地发挥硬件资源,提高程序的响应速度和处理能力。本文将介绍Java多线程异步并发的关键技术,帮助开发者更好地利用多核处理器,优化程序性能。
2、单线程实现
2.1 Thread
@Test
public void threadTest() {
Thread thread = new myThread();
thread.start();
}
public class myThread extends Thread {
@Override
public void run(){
System.out.println("Thread create");
}
}
通过继承Thread 重写run方法,其中run()方法的方法体代表了线程需要完成的任务,称之为线程执行体。通过调用线程对象引用的start()方法,使得该线程进入到就绪状态。
2.2 Runnable
@Test
public void runnableTest() {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("MyRunnable create");
}
}
创建Runnable实现类的实例,并以此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。
2.3 Callable和FutureTask
@Test
public void callableTest() throws ExecutionException, InterruptedException {
Callable<Integer> myCallable = new MyCallable(); // 创建MyCallable对象
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable);
Thread thread = new Thread(ft);
thread.start();
int result = ft.get();//当前是阻塞
}
public class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("MyCallable create");
return 0;
}
}
在实现Callable接口中,此时不再是run()方法了,而是call()方法,此call()方法作为线程执行体,同时还具有返回值!在创建新的线程时,是通过FutureTask来包装MyCallable对象,同时作为了Thread对象的target。
3、线程池的使用
线程池是一种管理线程的机制,它通过维护一定数量的线程,实现线程的复用和管理。在Java中,Executor框架提供了线程池的实现,使用线程池可以避免频繁创建和销毁线程,提高线程的利用率,降低系统开销。以下是线程池的基本用法:
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("解析-%d").build();
ThreadPoolExecutor executor = new ThreadPoolExecutor(30, 60, 60L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
for (int i =0 ; i < FileList.size(); i++){
DaFile daFile = FileList.get(i);
executor.execute(new Runnable() {
@Override
public void run() {
//执行线程中的业务
}
});
}
executor.shutdown();
while (true) {
if (executor.isTerminated() && executor.getQueue().size() == 0) {
//线程结束执行自己的业务
}}
3.1 newSingleThreadExecutor
创建一个单线程化的Executor
@Test
public void executeSingleTest() {
Runnable runnable = new MyRunnable();
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(runnable);
}
3.2 newCachedThreadPool
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
@Test
public void executeCacheTest() {
Runnable runnable = new MyRunnable();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(runnable);
}
3.3 newScheduledThreadPool
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
@Test
public void executeScheduledTest() {
Runnable runnable = new MyRunnable();
ExecutorService executorService = Executors.newScheduledThreadPool(10);
executorService.submit(runnable);
}
3.4 newFixedThreadPool
创建固定数目线程的线程池。
@Test
public void executeFixedTest() {
Runnable runnable = new MyRunnable();
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.submit(runnable);
}
4、CompletableFuture
CompletableFuture是Java 8引入的异步编程工具,它提供了一套丰富的API来处理异步任务。通过CompletableFuture,可以更方便地实现异步操作和并发控制。以下是一个简单的示例:
@Test
public void completableFutureTest() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(100);
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->{
System.out.println("查询图片");
return "image_path";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{
System.out.println("查询商品");
return "product";
});
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(()->{
System.out.println("查询SKU");
return "SKU";
});
//等待所有的运行完成,返回
CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2, future3);
allOf.get();
//任何一个执行完成,返回
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2, future3);
anyOf.get();
}
5、并发集合的使用
Java提供了一系列的并发集合,如ConcurrentHashMap、CopyOnWriteArrayList等,用于在多线程环境中安全地操作集合。使用这些并发集合可以避免在多线程环境中出现的竞态条件和数据不一致的问题。
ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("key", 1);
List<Integer> copyOnWriteList = new CopyOnWriteArrayList<>();
copyOnWriteList.add(1);
6、Lock和Condition的精细控制
Java的Lock和Condition接口提供了比传统的synchronized关键字更灵活的锁机制。使用Lock和Condition可以实现更精细的线程控制,如可中断锁、公平锁等。以下是一个简单的示例:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
// 等待条件满足
condition.await();
// 执行任务逻辑
} finally {
lock.unlock();
}
7、Fork/Join框架的并行计算
Fork/Join框架是Java 7引入的用于并行计算的框架,它通过将大任务拆分成小任务,并利用多核处理器并行执行这些小任务,提高计算性能。使用ForkJoinPool和RecursiveTask可以方便地实现并行计算。
public class MyRecursiveTask extends RecursiveTask<Integer> {
// 实现递归任务逻辑
}
ForkJoinPool forkJoinPool = new ForkJoinPool();
MyRecursiveTask myRecursiveTask = new MyRecursiveTask();
Integer result = forkJoinPool.invoke(myRecursiveTask);
8、结论
通过合理地使用上述技术,开发者可以更好地利用Java多线程特性,实现异步并发,提高程序性能。然而,在使用多线程时,需注意线程安全问题,避免出现死锁和竞态条件等问题。同时,通过合适的线程池大小、任务拆分策略等参数调优,可以进一步提升程序的性能。
评论区