Java线程,以及创建方式

Java线程,以及创建方式

1. 进程与线程的区别

  1. 进程是操作系统资源的最小分配单位, 线程是操作系统调度执行的基本单位
  2. 一个进程可能包括多个线程, 也就是说, 进程是包含线程的
  3. 每个进程都有独立的内存空间(虚拟地址空间), 此进程的多个线程(或一个), 共用这个进程的独立内存空间

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:一个由链表结构组成的双向阻塞队列。

较常用的是 LinkedBlockingQueueSynchronous,线程池的排队策略与 BlockingQueue 有关。

参数 6:threadFactory

线程工厂,主要用来创建线程,默认为正常优先级、非守护线程。

参数 7:handler

拒绝策略,拒绝处理任务时的策略,系统提供了 4 种可选:

  • AbortPolicy:拒绝并抛出异常。
  • CallerRunsPolicy:使用当前调用的线程来执行此任务。
  • DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务。
  • DiscardPolicy:忽略并抛弃当前任务。

默认策略为 AbortPolicy

总结

线程的使用, 尽量使用线程池, 线程池的使用尽量使用ThreadPoolExecutor.

ThreadPoolExecutor 最多可以设置 7 个参数,当然设置 5 个参数也可以正常使用,ThreadPoolExecutor 当任务过多(处理不过来)时提供了 4 种拒绝策略,也可以自定义拒绝策略

后面会写一篇详细的ThreadPoolExecutor 的文章

参考&鸣谢

https://juejin.cn/post/6907086699021631501#heading-2

https://juejin.cn/post/7023969128419426318

posted @   DevourZuan  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤
点击右上角即可分享
微信分享提示