序言
你只管努力,其他交给时间,时间会证明一切。
文章标记颜色说明:
- 黄色:重要标题
- 红色:用来标记结论
- 绿色:用来标记一级重要
- 蓝色:用来标记二级重要
希望这篇文章能让你不仅有一定的收获,而且可以愉快的学习,如果有什么建议,都可以留言和我交流
问题
思考一下这个问题,我们将围绕这三个问题来开始学习:
- 什么是多线程
- Java怎么实现多线程
- Java多线程示例
1 基础介绍
什么是多线程
多线程:
- 指的是在一个进程中同时运行多个线程,每个线程都可以独立执行不同的任务或操作。
- 与单线程相比,多线程可以提高程序的并发性和响应能力。
什么是进程
进程:
是指正在运行的程序的实例。
每个进程都拥有自己的内存空间、代码、数据和文件等资源,可以独立运行、调度和管理。在操作系统中,进程是系统资源分配的最小单位,是实现多任务的基础。
在Java中,每个Java虚拟机(JVM)都是一个进程,可以同时运行多个Java程序,每个Java程序都是一个独立的进程。
2 Java多线程
Java多线程是指在一个Java程序中同时执行多个线程,它可以提高程序的并发性和响应能力。Java中实现多线程的方式:
- 继承Thread类
- 实现Runnable接口
- Executor框架
- Callable
- Future
- 线程池
1 继承Thread类
继承Thread类是实现多线程的一种方式,只需要继承Thread类并重写run()方法即可。
public class ThreadDemo {
public static void main(String[] args) {
// 创建10个线程并启动
for (int i = 0; i < 10; i++) {
MyThread thread = new MyThread(i);
thread.start();
}
}
}
class MyThread extends Thread {
private int id;
public MyThread(int id) {
this.id = id;
}
public void run() {
System.out.println("Thread " + id + " is running");
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
以上代码中,首先创建了一个ThreadDemo类,在main函数中创建了10个线程,并启动这些线程。
每个线程都是MyThread类的实例,MyThread类继承了Thread类,并重写了run()方法,在方法中模拟了一个需要执行1秒钟的任务。
在main函数中,通过创建MyThread类的实例,并调用start()方法启动线程。start()方法会调用线程的run()方法,在run()方法中执行线程的任务。
2 实现Runnable接口
另一种实现多线程的方式是实现Runnable接口,需要实现run()方法,并将实现了Runnable接口的对象传递给Thread类的构造函数。
public class RunnableDemo {
public static void main(String[] args) {
// 创建10个线程并启动
for (int i = 0; i < 10; i++) {
Runnable task = new MyTask(i);
Thread thread = new Thread(task);
thread.start();
}
}
}
class MyTask implements Runnable {
private int id;
public MyTask(int id) {
this.id = id;
}
public void run() {
System.out.println("Thread " + id + " is running");
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
以上代码中,创建了一个RunnableDemo类,在main函数中创建了10个线程,并启动这些线程。
每个线程都是MyTask类的实例,MyTask类实现了Runnable接口,并重写了run()方法,在方法中模拟了一个需要执行1秒钟的任务。
在main函数中,通过创建MyTask类的实例,并创建一个Thread对象,将Runnable对象作为参数传递给Thread构造方法,最后调用start()方法启动线程。start()方法会调用线程的run()方法,在run()方法中执行线程的任务。
在使用实现Runnable接口实现多线程时,可以更好地分离任务和线程,并提高代码的可扩展性和可维护性。
如果需要添加更多的线程或任务,只需要创建更多的Runnable实例,并创建对应的Thread对象即可,不需要创建更多的线程类,并且可以更好地重用代码。
3 Executor框架
Executor框架是Java提供的一个线程池框架,用于管理和调度多个线程。通过Executor框架,可以更方便地实现多线程,避免手动管理线程带来的复杂性和风险。
Executor框架的核心接口是Executor和ExecutorService,
- Executor是一个简单的线程池接口,只有一个execute()方法,用于提交一个Runnable任务给线程池执行。
- ExecutorService是Executor的扩展接口,提供了更多的管理和调度线程的方法,如submit()、shutdown()、awaitTermination()等。
使用Executor框架实现多线程,通常需要以下步骤:
创建一个ExecutorService对象,可以使用Executors类提供的静态方法创建线程池,如newFixedThreadPool()、newCachedThreadPool()、newSingleThreadExecutor()等。
将需要执行的任务封装成一个Runnable或Callable对象,可以使用Java中的匿名内部类或Lambda表达式来创建。
将任务提交给ExecutorService对象执行,可以使用submit()方法提交Callable对象,或使用execute()方法提交Runnable对象。
在程序完成时,调用shutdown()方法关闭线程池,或使用awaitTermination()方法等待所有线程执行完毕。
下面是一个使用Executor框架实现多线程的示例:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorDemo { public static void main(String[] args) { // 创建一个包含10个线程的线程池 ExecutorService executor = Executors.newFixedThreadPool(10); // 提交10个任务给线程池执行 for (int i = 0; i < 10; i++) { executor.execute(new MyTask(i)); } // 关闭线程池 executor.shutdown(); } } class MyTask implements Runnable { private int id; public MyTask(int id) { this.id = id; } public void run() { System.out.println("Thread " + id + " is running"); try { Thread.sleep(1000); // 模拟任务执行时间 } catch (InterruptedException e) { e.printStackTrace(); } } }
代码讲解:
先创建了一个ExecutorDemo类,在main函数中创建了一个包含10个线程的线程池。
每个线程池中的线程都可以执行MyTask类的实例,MyTask类实现了Runnable接口,并重写了run()方法,在方法中模拟了一个需要执行1秒钟的任务。
在main函数中,创建MyTask类的实例,并调用ExecutorService的execute()方法提交给线程池执行。
execute()方法会将任务提交给线程池中的一个空闲线程执行。
最后调用ExecutorService的shutdown()方法关闭线程池。
需要注意的是,shutdown()方法会等待所有线程执行完毕后才会关闭线程池,如果需要立即关闭线程池,可以使用shutdownNow()方法。
Callable实现多线程
Callable是Java中的一个接口,与Runnable接口类似,都用于封装一个线程执行的任务。
不同的是,Callable接口的call()方法可以返回一个结果,而Runnable接口的run()方法没有返回值。
使用Callable实现多线程,通常需要以下步骤:
创建一个实现了Callable接口的类,实现call()方法,并在方法中编写线程执行的代码。
创建一个ExecutorService对象,可以使用Executors类提供的静态方法创建线程池,如newFixedThreadPool()、newCachedThreadPool()、newSingleThreadExecutor()等。
将Callable对象提交给ExecutorService对象执行,可以使用submit()方法提交。
调用Future对象的get()方法获取Callable线程执行的结果。
在程序完成时,调用shutdown()方法关闭线程池,或使用awaitTermination()方法等待所有线程执行完毕。
下面是一个使用Callable实现多线程的示例:
import java.util.concurrent.*;
public class CallableDemo {
public static void main(String[] args) throws Exception {
// 创建一个线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
// 提交10个Callable任务给线程池执行
Future<Integer>[] results = new Future[10];
for (int i = 0; i < 10; i++) {
Callable<Integer> task = new MyTask(i);
results[i] = executor.submit(task);
}
// 输出Callable任务的执行结果
for (int i = 0; i < 10; i++) {
Integer result = results[i].get();
System.out.println("Task " + i + " result is " + result);
}
// 关闭线程池
executor.shutdown();
}
}
class MyTask implements Callable<Integer> {
private int id;
public MyTask(int id) {
this.id = id;
}
public Integer call() throws Exception {
System.out.println("Task " + id + " is running");
Thread.sleep(1000); // 模拟任务执行时间
return id * 10;
}
}
首先创建一个线程池,然后提交10个Callable任务给线程池执行。每个Callable任务都是MyTask类的实例,MyTask类实现了Callable接口,并重写了call()方法,在方法中模拟了一个需要执行1秒钟的任务,并返回一个结果。
详细解释如下:
创建一个线程池,通过调用Executors的静态方法newFixedThreadPool(10),创建了一个固定大小为10的线程池。
在for循环中,通过创建MyTask类的实例,将其封装为Callable对象,并通过ExecutorService的submit()方法提交给线程池执行。submit()方法会返回一个Future对象,代表了Callable任务的执行结果。
在for循环中,通过Future数组记录每个Callable任务的执行结果,可以通过调用get()方法获取Callable任务的执行结果。如果Callable任务还没有执行完成,get()方法会阻塞当前线程,直到任务执行完成并返回结果。如果任务执行过程中发生异常,get()方法会抛出ExecutionException异常。
在任务完成后,可以通过调用Future对象的get()方法获取任务的执行结果,并打印输出。
最后调用ExecutorService的shutdown()方法关闭线程池,应该在所有任务执行完成后才能关闭线程池。
注意,在使用Callable实现多线程时,要考虑线程安全、同步机制、任务调度和管理等问题,以确保程序的正确性和稳定性。
同时,由于Callable任务的执行时间可能会比较长,可以设置超时时间来避免任务执行时间过长导致的程序阻塞。
Future实现多线程
Future是Java中的一个接口,用于异步获取任务执行结果。
在多线程编程中,可以使用Future来获取异步任务的执行结果,以便在任务完成后进行处理或展示。
使用Future实现多线程,需要以下步骤:
创建一个实现了Callable接口的类,实现call()方法,并在方法中编写线程执行的代码。
创建一个ExecutorService对象,可以使用Executors类提供的静态方法创建线程池,如newFixedThreadPool()、newCachedThreadPool()、newSingleThreadExecutor()等。
将Callable对象提交给ExecutorService对象执行,可以使用submit()方法提交,submit()方法会返回一个Future对象。
调用Future对象的get()方法获取Callable线程执行的结果。如果任务还没有执行完成,get()方法会阻塞当前线程直到任务执行完成并返回结果。
代码示例:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class FutureDemo {
public static void main(String[] args) throws Exception {
// 创建一个线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
// 提交10个Callable任务给线程池执行
List<Future<Integer>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Callable<Integer> task = new MyTask(i);
Future<Integer> result = executor.submit(task);
results.add(result);
}
// 输出Callable任务的执行结果
for (int i = 0; i < 10; i++) {
Integer result = results.get(i).get();
System.out.println("Task " + i + " result is " + result);
}
// 关闭线程池
executor.shutdown();
}
}
class MyTask implements Callable<Integer> {
private int id;
public MyTask(int id) {
this.id = id;
}
public Integer call() throws Exception {
System.out.println("Task " + id + " is running");
Thread.sleep(1000); // 模拟任务执行时间
return id * 10;
}
}
示例讲解
在以上示例中:
- 首先创建了一个线程池,然后提交10个Callable任务给线程池执行。每个Callable任务都是MyTask类的实例,MyTask类实现了Callable接口,并重写了call()方法,在方法中模拟了一个需要执行1秒钟的任务,并返回一个结果。
- 在main函数中,使用List记录每个Callable任务的执行结果的Future对象,并在任务完成后通过调用get()方法获取Callable任务的执行结果。如果任务还没有执行完成,get()方法会阻塞当前线程直到任务执行完成并返回结果。
- 最后关闭线程池。
线程池实现多线程
线程池是Java中提供的一个用于管理和复用多个线程的框架,可以有效地提高多线程应用程序的性能和可靠性。
使用线程池实现多线程,通常需要以下步骤:
创建一个线程池,可以使用Executors类提供的静态方法创建线程池,如newFixedThreadPool()、newCachedThreadPool()、newSingleThreadExecutor()等。
创建一个实现了Runnable接口或Callable接口的类,实现run()方法或call()方法,并在方法中编写线程执行的代码。
将Runnable对象或Callable对象提交给线程池执行,可以使用submit()方法提交,submit()方法会返回一个Future对象。
关闭线程池,可以调用shutdown()方法或shutdownNow()方法。
代码示例:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) throws Exception {
// 创建一个包含10个线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
// 提交10个任务给线程池执行,并记录每个任务的执行结果
List<Future<Integer>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Callable<Integer> task = new MyTask(i);
Future<Integer> result = executor.submit(task);
results.add(result);
}
// 等待所有任务执行完成
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
// 输出所有任务的执行结果
int total = 0;
for (int i = 0; i < 10; i++) {
try {
Integer result = results.get(i).get();
System.out.println("Task " + i + " result is " + result);
total += result;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
System.out.println("Task " + i + " execution error: " + e.getCause().getMessage());
}
}
System.out.println("Total result is " + total);
}
}
class MyTask implements Callable<Integer> {
private int id;
public MyTask(int id) {
this.id = id;
}
public Integer call() throws Exception {
System.out.println("Task " + id + " is running");
Thread.sleep(2000); // 模拟任务执行时间
if (id % 2 == 0) {
throw new RuntimeException("Task " + id + " execution error");
}
return id * 10;
}
}
示例讲解:
在以上示例中,首先创建了一个包含10个线程的线程池,然后提交10个任务给线程池执行。每个任务都是MyTask类的实例,MyTask类实现了Callable接口,并重写了call()方法,在方法中模拟了一个需要执行2秒钟的任务,并返回一个结果。
其中,如果任务的id是偶数,会抛出一个运行时异常。
在main函数中,使用List记录每个任务的执行结果的Future对象,并在任务完成后通过调用get()方法获取任务的执行结果。
如果任务还没有执行完成,get()方法会阻塞当前线程直到任务执行完成并返回结果。
在所有任务提交给线程池后,调用ExecutorService的shutdown()方法关闭线程池,并调用awaitTermination()方法等待所有任务执行完成。
最后输出所有任务的执行结果,并计算所有任务的执行结果的总和。
总结
在多线程编程中,线程安全是一个重要的问题。后面文章会详细讲解:
- Java中的同步机制,如synchronized关键字、Lock接口等,
- 线程之间的通信机制,如wait()、notify()、notifyAll()等。
- 程序如何避免死锁、竞态条件等问题,以确保程序的正确性和稳定性。
总之,Java多线程是提高程序并发性和响应能力的重要手段,需要掌握多线程的实现方式、同步机制、线程之间的通信机制等,以确保多线程程序的正确性和稳定性。
图书推荐
图书介绍
图书名称:《Java核心技术 卷Ⅱ 最新版》
内容介绍
Java诞生28年来,这本享誉全球的 Java 经典著作《Core Java》一路伴随着 Java 的成长,得到了百万 Java 开发者的青睐,成为一本畅销不衰的Java经典图书,影响了几代技术人。
最新版中文版《Java核心技术(原书第12版)经全面修订,以涵盖Java 17的新特性。新版延续之前版本的优良传统,用数百个实际的工程案例,全面系统地讲解了Java语言的核心概念、语法、 重要特性、 开发方法。
着力让读者在充分理解Java语言和Java类库的基础上,灵活应用Java提供的高级特性,具体包括面向对象程序设计、反射与代理、接口与内部类、异常处理、泛型程序设计、集合框架、事件监听器模型、图形用户界面设计和并发。
Java 之父先前也说,开发者应尽快弃用 JDK 8,可以选择 JDK 17 长期支持版本。针对 Java 17 新特性全面更新的《Core Java》最新版第12版中文版《Java核心技术·卷Ⅰ开发基础(原书第12版)》自去年5月上市以来,一经发布就引起了轰动,得到数万读者的高度关注 ,大家纷纷留言都在盼望卷Ⅱ的上市!
卷Ⅱ针对Java 17的新特性和改进进行了修订。与以往一样,所有的章节都做了全面更新,移除了过时的内容,并且详细讨论了各种新API。
卷| 与 卷|| 的区别
学完本书,你将成为一个真正的 Java 程序员。本书不仅让你深入了解设计和实现 Java 应用涉及的所有基础知识和 Java 特性,还会帮助你掌握开发 Java 程序所需的全部基本技能。相信在学习Java的道路上有了本书的辅助,你的学习一定可以做到事半功倍。
等不及的小伙伴,也可以点击下方链接,先睹为快:
Java核心技术 卷II:高级特性 原书第12版
参与方式
图书数量:本次送出 3 本 !!!⭐️⭐️⭐️
活动时间:截止到 2023-05-12 12:00:00抽奖方式:
- 2本,留言+该留言论赞数的前两名各获得一本!
- 1本,评论区随机挑选一位小伙伴送书一本!
- 留言内容:“用心生活,用力向上,微笑前行,就是对生活最好的回馈。”
参与方式:关注博主、点赞、收藏,评论区留言
中奖名单
🍓🍓 获奖名单🍓🍓
中奖名单:请关注博主动态
名单公布时间:2023-05-12 下午