网站建设资金请示,源码出售网站,国际网站怎么开通,订房网站开发Java多线程是Java语言中一个非常重要的特性#xff0c;它允许程序同时执行多个任务。通过多线程#xff0c;程序可以同时处理多项任务#xff0c;从而缩短程序的执行时间。另外#xff0c;多线程也有助于利用多核处理器#xff0c;更好地发挥计算机硬件的性能。
那我们在…Java多线程是Java语言中一个非常重要的特性它允许程序同时执行多个任务。通过多线程程序可以同时处理多项任务从而缩短程序的执行时间。另外多线程也有助于利用多核处理器更好地发挥计算机硬件的性能。
那我们在Java中如何去实现多线程呢
今天我们就从最简单的的Thread、Runnable讲到现在比较常用的CompletableFuture
Thread
Java中通过创建Thread类的实例来创建线程。创建线程的最简单方式是扩展Thread类并覆盖run()方法。在run()方法中编写要执行的代码然后在程序中调用start()方法启动该线程。例如
public class MyThread extends Thread {public void run() {// 线程要执行的代码for (int i 0; i 10; i) {System.out.println(id: Thread.currentThread().getId() ,name: Thread.currentThread().getName() { i });}}
}public static void main(String[] args) {System.out.println(start);MyThread myThread1 new MyThread();MyThread myThread2 new MyThread();myThread1.start();myThread2.start();System.out.println(end);
}当然你要是嫌麻烦的话你可以直接使用匿名内部类 // 效果是一样的System.out.println(start);new Thread(() - {for (int i 0; i 10; i) {System.out.println(id: Thread.currentThread().getId() ,name: Thread.currentThread().getName() { i });}}).start();new Thread(() - {for (int i 0; i 10; i) {System.out.println(id: Thread.currentThread().getId() ,name: Thread.currentThread().getName() { i });}}).start();System.out.println(end);
这边注意千万不用使用run()方法这会使线程变成同步同时如果你想让多线程的内容执行完之后再去执行之后的代码那你可以使用join()这个方法它可以使该线程执行完之后再去执行下面的操作。 当然你也可以使用sleep() 让主线程睡眠一段时间当然这个睡眠时间是主观的时间是我们自己定的这个方法不推荐
Runnable
此外我们还可以使用Runnable接口来创建线程。Runnable接口只有一个run()方法在其中编写要执行的代码然后将Runnable对象作为参数传递给Thread类的构造函数并调用start()方法启动线程。例如
public class MyThread implements Runnable {Overridepublic void run() {// 线程要执行的代码for (int i 0; i 10; i) {System.out.println(id: Thread.currentThread().getId() ,name: Thread.currentThread().getName() { i });}}
}其实和Thread差不多只是一个实现接口一个继承类直接使用的话这两个没有太大的区别 不过有的地方会说Runnable便于实现资源共享而Thread不能但是经过的我的测试认为Thread也可以实现资源共享 测试代码 public class MyThread extends Thread {private int ticket 5;Overridepublic void run() {// 双检索机制——保证线程的安全if (ticket 0) {synchronized (this) {if (ticket 0) {while (true) {System.out.println(Thread: Thread.currentThread().getName() --Thread ticket ticket--);if (ticket 0) {break;}}}}}}}public class Test {public static void main(String[] args) {MyThread myThread1 new MyThread();new Thread(myThread1).start();new Thread(myThread1).start();new Thread(myThread1).start();new Thread(myThread1).start();new Thread(myThread1).start();new Thread(myThread1).start();}
}执行结果如下
Thread:Thread-1--Thread ticket 5
Thread:Thread-1--Thread ticket 4
Thread:Thread-1--Thread ticket 3
Thread:Thread-1--Thread ticket 2
Thread:Thread-1--Thread ticket 1
Thread:Thread-1--Thread ticket 0我们看Thread的源代码发现其实Thread也就是实现了Runnable接口提供了更多的方法而已。所以说Thread与Runnable并没有什么区别。如果硬要说有什么区别的话那就是类与接口的区别继承与实现的区别
Callable搭配Future
对于子线程我们有时候可能会有两种需求
获取子线程运行结果获取子线程运行状态成功、失败、异常
Thread和Runnable都不满足这两个要求Runnable可以获取状态但不能获取结果于是出现了Callable。Callable配合Future使用可以获得子线程执行结果。 Future是Java多线程中的一种异步计算方式可以用来获取在执行任务期间进行异步计算的结果
public class MyThread implements CallableInteger {private Integer number;public Integer getNumber(){return number;}public MyThread (Integer number) {this.number number;}Overridepublic Integer call() throws Exception {int result 0;for (int i 0; i number; i) {result ;System.out.println(id: Thread.currentThread().getId() ,name: Thread.currentThread().getName() { result });}return result;}
}public static void main(String[] args) throws Exception {System.out.println(start);MyThread myThread1 new MyThread (10);MyThread myThread2 new MyThread (15);//使用Executors工厂类创建一个简单的线程池线程数2ExecutorService executor Executors.newFixedThreadPool(2);// 将任务提交给线程池 FutureInteger submit1 executor.submit(myThread1);FutureInteger submit2 executor.submit(myThread2);// 获取任务的执行结果System.err.println(submit1.get());System.err.println(submit2.get());executor.shutdown();System.out.println(end);}在这个例子中我们实现了MyThread 类它实现了Callable接口并重写了call()方法。在call()方法中我们模拟了一个长时间运行的任务并返回一个计算结果作为执行结果。在main方法中我们首先创建了一个固定线程数的线程池ExecutorService然后将MyThread的实例传入submit方法来提交任务并得到一个Future类型的对象。通过调用该对象的get()方法我们可以获取任务的执行结果。最后我们关闭了线程池。
需要注意的是由于调用Future的get()方法是阻塞的所以我们可能需要对其进行try-catch处理。此外在完成任务后我们必须关闭线程池以释放资源。
这是一个简单的使用Callable的示例通过组合多个Future对象可以实现更复杂的并发计算。
Future
当然 Future也可以进行单独使用。 在使用Future时可以调用get()方法来阻塞等待任务执行完毕并获取计算结果也可以调用isDone()方法来判断任务是否执行完毕如果任务执行过程中发生异常可以通过调用get()方法获取异常信息
public static void main(String[] args) throws Exception {System.out.println(start);ExecutorService executor Executors.newFixedThreadPool(2);Future future1 executor.submit(() - {int result 0;for (int i 0; i number; i) {result ;System.out.println(id: Thread.currentThread().getId() ,name: Thread.currentThread().getName() { result });}return result;});Future future2 executor.submit(() - {int result 0;for (int i 0; i number; i) {result ;System.out.println(id: Thread.currentThread().getId() ,name: Thread.currentThread().getName() { result });}return result;});String result1 (String) future1.get();String result2 (String) future2.get();System.out.println(result1);System.out.println(result2);executor.shutdown();System.out.println(end);}其实和上一个方法是一样的只不过是这边把call()方法中的业务逻辑冗余到了主代码中耦合性更高了如果只是一些简单的代码可以使用复杂的话还是建议搭配callable 一起使用比较好 CompletableFuture CompletableFuture是Java 8引入的一个非常有用的功能它可以让我们更轻松地处理异步操作特别是当这些操作涉及到多个阶段时。 CompletableFuture 继承自 Future 接口可以在计算完成后获取计算结果因此它也具备了 Future 的特性。除此之外它还提供了一些其他的特性
异步执行和串行执行功能任务执行完毕后可以触发观察者机制可以将两个任务组合到一起执行等处理更加复杂的异步场景。
现在开发来说使用较为复杂的逻辑普遍会使用CompletableFuture 创建CompletableFuture 我们可以使用runAsync()方法直接去异步执行
CompletableFuture.runAsync(() - {// 在这里执行一些耗时的操作});异步处理CompletableFuture的结果 我们可以使用CompletableFuture的静态方法supplyAsync()创建一个异步执行的CompletableFuture并提供一个lambda表达式作为其计算发生器
CompletableFutureString completableFuture CompletableFuture.supplyAsync(() - {// 在这里执行一些耗时的操作return some result;
});completableFuture.thenAccept(result - {System.out.println(Got result: result);
});
// 这里可以继续执行其他任务completableFuture将在后台继续执行并在完成后触发回调函数。操作两个或多个CompletableFuture 如果我们需要等待多个CompletableFuture都完成后执行某些任务那么我们可以使用CompletableFuture的静态方法allOf()或anyOf()
CompletableFutureString future1 CompletableFuture.supplyAsync(() - {// 在这里执行一些耗时的操作return Result of future 1;
});CompletableFutureString future2 CompletableFuture.supplyAsync(() - {// 在这里执行一些耗时的操作return Result of future 2;
});CompletableFutureVoid allFutures CompletableFuture.allOf(future1, future2);allFutures.thenRun(() - {//这边join和get方法都可以 只是抛出异常的区别String result1 future1.join();String result2 future2.join();System.out.println(Got both results: result1 and result2);
});CompletableFuture 默认使用的是ForkJoinPool线程池如果你想自己定义线程池的话可以使用Executors直接创建一个但是Executors 是一个工厂类提供了一些静态方法用于创建线程池而不是一个线程池本身因此无法设置线程的详细参数例如核心线程数最大线程数拒绝策略等。如果需要设置这些参数应该使用 ThreadPoolExecutor 类来手动创建线程池 /*** public ThreadPoolExecutor(int corePoolSize,* int maximumPoolSize,* long keepAliveTime,* TimeUnit unit,* BlockingQueueRunnable workQueue,* ThreadFactory threadFactory,* RejectedExecutionHandler handler) {}* 1. corePoolSize核心线程数当提交的任务数大于 corePoolSize 时线程池会自动扩容。** 2. maximumPoolSize线程池最大线程数当活动线程数达到该值并且 workQueue 队列已满则执行拒绝策略。** 3. keepAliveTime线程空闲超时时间超过该时间则进行回收。** 4. unitkeepAliveTime 的时间单位。** 5. workQueue任务阻塞队列用于存储提交但尚未执行的任务。** 6. threadFactory线程工厂用于创建线程。** 7. handler拒绝策略当线程数量已经达到 maximumPoolSize 并且队列已满时采取的策略。*/ThreadPoolExecutor threadPoolExecutor new ThreadPoolExecutor(5,10,10,TimeUnit.SECONDS,new LinkedBlockingQueue(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());在springboot中有个方法也用的很频繁——async,只需要在方法上加上这个注解并在启动类上加上Enableasync 就可以直接实现多线程异常操作不过这个多线程的方法和上面讲的还是有些区别的async 是调用方法的时候进行多线程异步操作但是方法中的业务逻辑还是同步的所以正常可以搭配ComplatableFuture一起使用当然也可以吧中间的业务逻辑提取出方法再加个async 哈哈哈哈 都可以的 Java的多线程机制是Java编程中不可或缺的一部分了解和熟悉Java多线程编程能力将极大地提升程序员的工作效率和编程水平。