Java线程,以及创建方式
Java线程,以及创建方式
1. 进程与线程的区别
- 进程是操作系统资源的最小分配单位, 线程是操作系统调度执行的基本单位
- 一个进程可能包括多个线程, 也就是说, 进程是包含线程的
- 每个进程都有独立的内存空间(虚拟地址空间), 此进程的多个线程(或一个), 共用这个进程的独立内存空间
2. 创建进程的方式
2.1 普通创建线程
2.1.1 继承Thread类
/* 多线程的实现方式第一种:继承方式 1、定义一个类,继承Thread类,Thread类是一个线程类,用于开启线程 2、步骤: (1) 自定一个类MyThread,继承Thread类 (2) 重写Thread类中的run方法,用于定义新的要执行的内容 (3) 创建自定义类型的对象 (4) 调用线程开启的方法,start()方法 */ //1.定义一个类继承自Thread类 class MyThread extends Thread { //2.重写run方法 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(i); System.out.println("继承Thread实现的线程"); } } } public class Demo01 { public static void main(String[] args) { MyThread myThread =new MyThread(); //myThread.run(); //这样写是错误的,开启多线程应该使用start myThread.start(); for (int i = 0; i < 100; i++) { System.out.println(i); System.out.println("主线程"); } new MyThread().start(); } }
创建自定义类继承Thread类, 并重写run方法(使用匿名内部类的方式)
/* 多线程的实现方式第一重的第二种:匿名内部类的形式实现多线程 */ public class Demo02 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(){ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(i); } } }; thread.start(); for (int i = 0; i < 100; i++) { Thread.sleep(1); System.out.println("休息"); } } }
2.1.2 实现Runable接口, 重写run方法
/* 多线程的实现的第二种方式:实现接口 1、实现Runnable接口:Runnable接口的实现类对象,表示一个具体的任务,将来创建一个线程对象,让线程对象执行这个任务 2、步骤: (1) 定义一个任务类MyRunnable,实现Runnable接口 (2) 重写接口中的run方法,用于定义任务的内容 (3) 创建任务类对象,表示任务 (4) 创建Thread类对象,创建线程,将任务对象作为构造方法的参数传递,用于执行任务的类对象 Thread(Runnable r) (5) 调用线程开启的方法,start() 开启新线程 */ //(1) 定义一个任务类MyRunnable,实现Runnable接口 class MyRunnable implements Runnable { private String name; @Override //(2) 重写接口中的run方法,用于定义任务的内容 public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName() + "打了" + i + "小兵"); } } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Demo03 { public static void main(String[] args) { //(3) 创建任务类对象,表示任务 MyRunnable myRunnable = new MyRunnable(); myRunnable.setName("鲁班"); //创建Thread类对象,创建线程 //(4) 创建Thread类对象,创建线程,将任务对象作为构造方法的参数传递,用于执行任务的类对象 Thread(Runnable r) Thread thread = new Thread(myRunnable); //启动线程 //(5) 调用线程开启的方法,start() 开启新线程 thread.start(); //任务 MyRunnable myRunnable2 = new MyRunnable(); myRunnable2.setName("貂蝉"); //创建Thread类对象,创建线程 Thread thread2 = new Thread(myRunnable2); //启动线程 thread2.start(); //任务 MyRunnable myRunnable3 = new MyRunnable(); myRunnable3.setName("刘备"); //创建Thread类对象,创建线程 Thread thread3 = new Thread(myRunnable3); //启动线程 thread3.start(); //Runable是一个接口,也可以通过匿名内部类的方式进行实现 Runnable runnable = new Runnable() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(i); } } }; Thread thread4 = new Thread(runnable); thread4.start(); } }
实现Runable接口, 重写run方法(使用匿名内部类的方式)
/* 多线程的实现方式第一重的第二种:匿名内部类的形式实现多线程 */ public class Demo04 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(){ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(i); } } }; thread.start(); for (int i = 0; i < 100; i++) { Thread.sleep(1); System.out.println("休息"); } } }
2.1.3 实现Callable接口
/* 多线程的第三种实现方式:实现Callable接口 1、相关的方法: (1) V call(): 计算结果,如果无法计算结果,则抛出一个异常 (2) FutureTask(Callable<V> cal):创建一个FutureTask,一旦运行就执行给定的Callable (3) V get(): 获取计算完成的返回值 2、实现思路: (1) 如果创建Thread,执行Runnable任务,需要Runnable对象 (2) Runnable是一个接口,有一个特殊的实现类 (3) FutureTask是Runnable的实现类 (4) FutureTask在创建对象的时候,需要传递Callable类型的对象 (5) Callable是一个接口,所以需要一个Callable实现类对象 (6) Callable接口中提供了一个方法,call() 相当于原来的run,但是该方法是可以有返回值的 3、步骤: (1) 定义一个MyCallable实现Callable接口 (2) 在实现类MyCallable中重写Call方法 (3) 创建MyCallable对象 (4) 创建FutureTask对象,把MyCallable的对象作为构造方法的参数传递给FutureTask (5) 创建Thread类的对象,把FutureTask对象作为构造方法的参数 (6) 使用Thread类调用start方法启动线程 (7) FutureTask对象调用get方法,可以获取线程执行的任务,这个任务最终返回的结果 */ //1. 定义一个类去实现Callable接口 class MyCallable implements Callable<Integer> { //2. 重写call方法 @Override public Integer call() throws Exception { Integer sum = 0; for (int i = 0; i < 100; i++) { System.out.println(i + "Callable"); sum += i; } return sum; } } public class Demo05 { public static void main(String[] args) throws ExecutionException, InterruptedException { //3. 创建MyCallable对象 , 也就是实现类的对象 MyCallable myCallable = new MyCallable(); //4. 创建FutureTask对象, 并把第一步创建的实现类传入其对象的构造方法中 FutureTask futureTask = new FutureTask<>(myCallable); //5. 创建Thread类的对象, 把第四步的FutureTask对象传入其对象的构造方法中 Thread thread = new Thread(futureTask); //6. Thread对象启动线程 thread.start(); //7. 可以使用FutureTask对象调用get方法,可以获取线程执行的任务,这个任务最终返回的结果 //查看源码,返回值为Value,此处应该为Integer System.out.println(futureTask.get()); } }
2.1.4 使用Lambda表达式
public class Demo06 { public static void main(String[] args) throws InterruptedException { // Thread t1 = new Thread(() -> System.out.println("使用匿名类")); Thread t2 = new Thread(() -> { for (int i = 0; i < 100; i++) { System.out.println("使用匿名类创建 Thread 子类对象"); } }); t1.start(); t2.start(); } }
2.2 线程池创建线程
2.2.1 FixedThreadPool
创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
public static void executorServiceTest() { // 创建线程池 2个数据级 ExecutorService threadPool = Executors.newFixedThreadPool(2); // 创建任务 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("执行:" + Thread.currentThread().getName()); } }; // 执行 // 执行方式: submit & execute threadPool.submit(runnable); threadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable); }
2.2.2 CachedThreadPool
创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
public static void cachedThreadPoolTest() { // 创建线程池 ExecutorService threadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { // 创建任务 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("执行:" + Thread.currentThread().getName()); } }; threadPool.execute(runnable); } }
2.2.3 ScheduledThreadPool
执行延迟任务
public static void scheduledThreadPoolTest() { // 创建线程池 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5); // 此线程池, 可以执行延迟任务 // 现在设置5s后执行打印任务 System.out.println("开始执行:" + new Date()); executorService.schedule( () -> { System.out.println("任务执行中:" + new Date()); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } , 1 , TimeUnit.SECONDS); }
2.2.4 newWorkStealingPool
创建一个抢占式执行的线程池(任务执行顺序不确定),注意此方法只有在 JDK 1.8+ 版本中才能使用。
public static void newWorkStealingPoolTest() { // 创建线程池 ExecutorService threadPool = Executors.newWorkStealingPool(); for (int i = 0; i < 10; i++) { int index = i; // 创建任务 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("执行:" + index + "---" +Thread.currentThread().getName()); } }; threadPool.execute(runnable); } // 此时发现任务的执行顺序是不确定的,因为它是抢占式执行的。 // 为确保执行完成 while (!threadPool.isTerminated()) { } }
2.2.5 SingleThreadExecutor
创建单个线程数的线程池,它可以保证先进先出的执行顺序。
public static void singleThreadExecutorTest() { // 创建线程池 ExecutorService threadPool = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { int index = i; // 创建任务 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("执行:" + index); } }; threadPool.execute(runnable); } }
2.2.6 SingleThreadScheduledExecutor
创建一个单线程的可以执行延迟任务的线程池。
使用示例如下:
public static void SingleThreadScheduledExecutor() { // 创建线程池 ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor(); // 添加定时执行任务(2s 后执行) System.out.println("添加任务,时间:" + new Date()); threadPool.schedule(() -> { System.out.println("任务被执行,时间:" + new Date()); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { } }, 2, TimeUnit.SECONDS); }
2.2.7 ThreadPoolExecutor
最原始的创建线程池的方式,它包含了 7 个参数可供设置。
// 最原始的创建线程池的方式,它包含了 7 个参数可供设置。 public static void myThreadPoolExecutorTest() { // 创建线程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10)); // 执行任务 for (int i = 0; i < 10; i++) { final int index = i; threadPool.execute(() -> { System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }); } }
ThreadPoolExecutor 参数介绍
ThreadPoolExecutor 最多可以设置 7 个参数,如下代码所示:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { // 省略... }
参数名 | 说明 |
---|---|
corePoolSize | 核心线程数,线程池中始终存活的线程数。 |
maximumPoolSize | 最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数。 |
keepAliveTime | 最大线程数可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。 |
unit | 单位是和参数 3 存活时间配合使用的,合在一起用于设定线程的存活时间 |
workQueue | 一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全 |
threadFactory | 线程工厂,主要用来创建线程,默认为正常优先级、非守护线程。 |
handler | 拒绝策略,拒绝处理任务时的策略 |
参数 4:unit:
单位是和参数 3 存活时间配合使用的,合在一起用于设定线程的存活时间 ,参数 keepAliveTime 的时间单位有以下 7 种可选:
- TimeUnit.DAYS:天
- TimeUnit.HOURS:小时
- TimeUnit.MINUTES:分
- TimeUnit.SECONDS:秒
- TimeUnit.MILLISECONDS:毫秒
- TimeUnit.MICROSECONDS:微妙
- TimeUnit.NANOSECONDS:纳秒
参数 5:workQueue
一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全,它包含以下 7 种类型:
- ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
- SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
- PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
- DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
较常用的是 LinkedBlockingQueue
和 Synchronous
,线程池的排队策略与 BlockingQueue
有关。
参数 6:threadFactory
线程工厂,主要用来创建线程,默认为正常优先级、非守护线程。
参数 7:handler
拒绝策略,拒绝处理任务时的策略,系统提供了 4 种可选:
- AbortPolicy:拒绝并抛出异常。
- CallerRunsPolicy:使用当前调用的线程来执行此任务。
- DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务。
- DiscardPolicy:忽略并抛弃当前任务。
默认策略为 AbortPolicy
。
总结
线程的使用, 尽量使用线程池, 线程池的使用尽量使用ThreadPoolExecutor.
ThreadPoolExecutor
最多可以设置 7 个参数,当然设置 5 个参数也可以正常使用,ThreadPoolExecutor
当任务过多(处理不过来)时提供了 4 种拒绝策略,也可以自定义拒绝策略
后面会写一篇详细的ThreadPoolExecutor
的文章
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤