Java多线程01——多线程的创建

时间:2023-02-04 22:55:10

1 进程和线程

进程:

  • 进程是并发执行程序在执行过程中,资源分配和管理的基本单位。
  • 进程可以理解为一个应用程序的执行过程,应用程序一旦执行,就是一个进程。

线程:

  • 线程是进程的一个执行单元,是进程内可调度实体。
  • 线程是比进程更小的独立运行的基本单位。
  • 线程也被称为轻量级进程。

二者的区别:

名称

进程

线程

地址空间

不同的进程之间的地址空间是独立的

同一进程的所有线程共享本进程的地址空间

资源拥有

进程之间的资源是独立的,无法共享

同一进程的所有线程共享本进程的资源

执行过程

每一个进程可以说就是一个可执行的应用程序

线程不能够独立执行,必须依存在应用程序中

2 创建线程的五种方式

Java多线程01——多线程的创建

2.1 继承 ​​Thread​​ 类

通过继承​​Thread​​并且重写其​​run()​​方法,​​run()​​方法中定义需要执行的任务。

创建后的子类通过调用​​start()​​方法即可执行线程方法。

注意: 通过继承​​Thread​​创建的线程类,多个线程间无法共享线程类的实例变量。 需要创建不同Thread对象,自然不共享资源。

/*
定义线程类,继承Thread
重写run()方法
创建线程类对象
调用start()方法启动线程
*/
public class UserThread extends Thread {
@Override
public void run() {
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName() + " is running " + i);
}
}
}

public class TestThread {
public static void main(String[] args) {
for(int i=0;i<2;i++){
new UserThread().start();
}
}
}

输出如下:

Thread-0 is running 0

Thread-1 is running 0

Thread-1 is running 1

Thread-1 is running 2

Thread-0 is running 1

Thread-0 is running 2

2.2 实现 ​​Runnable​​ 接口

需要先定义一个类实现 ​​Runnable​​ 接口并重写该接口的 ​​run()​​ 方法,此​​run()​​方法是线程执行体。

接着创建​​Runnable​​实现类的对象,作为创建​​Thread​​对象的参数​​target​​,此​​Thread​​对象才是真正的线程对象。

利用实现​​Runnable​​接口的线程类创建对象,可以实现线程之间的资源共享

/*
定义线程类,实现 Runnable接口
重写run()方法
创建实现类对象
创建Thread类,并将线程类对象参数传入Thread构造方法中
启动线程
*/
public class UserRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<3;i++) {
System.out.println(Thread.currentThread().getName() + " is running" + i);
}
}
}
public class TestUserRunnable {
public static void main(String[] args) {
UserRunnable userRunnable = new UserRunnable();
new Thread(userRunnable).start();
new Thread(userRunnable).start();
}
}

输出如下:

Thread-0 is running 0

Thread-1 is running 0

Thread-1 is running 1

Thread-1 is running 2

Thread-0 is running 1

Thread-0 is running 2

2.3 实现 ​​Callable​​ 接口实现带有返回值的线程

​Callable​​ 接口如同 ​​Runnable​​ 接口的升级版,其提供的 ​​call() ​​方法将作为线程的执行体,同时允许有返回值

​Callable​​ 对象不能直接作为 ​​Thread​​ 对象的target,因为 ​​Callable​​ 接口是 Java5 新增接口,不是 ​​Runnable​​ 接口的子接口。

对于这个问题的解决方案,就引入 ​​Future​​ 接口,此接口可以接受 ​​call()​​ 的返回值,​​RunnableFuture​​ 接口是 ​​Future​​ 接口和 ​​Runnable​​ 接口的子接口,可以作为 ​​Thread​​ 对象的target。

import java.util.concurrent.Callable;

/**
* 定义线程类UserCallable,实现Callable接口
* 重写call()方法
* 创建UserCallable对象
* 创建 FutureTask(实现了接口 RunnableFuture) 的对象,构造函数的参数是 UserCallable 的对象
* 创建 Thread 对象,构造函数的参数是 FutureTask 对象
* 启动线程
*/
public class UserCallable implements Callable {
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName() + " 启动!");
return "我重写了call方法";
}
}

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TestUserCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
UserCallable userCallable = new UserCallable();
FutureTask futureTask = new FutureTask(userCallable);
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}

输出如下:

Thread-0 启动!

我重写了call方法

2.4 继承 ​​TimerTask​

​Timer​​ 和 ​​TimerTask​​ 可以作为实现线程的另一种方式。

​Timer​​ 是一种线程设施,用于安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行,可以看成一个定时器,可以调度 ​​TimerTask​​。

​TimerTask​​ 是一个抽象类,实现了 ​​Runnable​​ 接口,所以具备了多线程的能力。

多线程类

import java.util.Date;
import java.util.TimerTask;

/**
* 创建 UserTimer 类,继承 TimerTask 抽象类
* 创建 UserTimer 对象
* 创建 Timer 类对象,设置任务的执行策略
*/
public class UserTimer extends TimerTask {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running " + new Date());
}
}

测试类

import java.util.Timer;

public class TestUserTimer {
public static void main(String[] args) {
UserTimer userTimer = new UserTimer();
Timer timer = new Timer();
timer.schedule(userTimer, 3000, 2000);
}
}

输出如下:

Timer-0 is running Sat Sep 18 23:10:47 CST 2021

Timer-0 is running Sat Sep 18 23:10:49 CST 2021

Timer-0 is running Sat Sep 18 23:10:51 CST 2021

Timer-0 is running Sat Sep 18 23:10:53 CST 2021

Timer-0 is running Sat Sep 18 23:10:55 CST 2021

Timer-0 is running Sat Sep 18 23:10:57 CST 2021

2.5 通过线程池启动多线程

通过 ​​Executors​​ 的工具类可以创建线程池。

提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行。

降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗。

方便线程并发数的管控,因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM(内存溢出),并且会造成CPU过度切换。

2.5.1 线程池一:固定大小的线程池​​FixThreadPool(int n)​

创建有固定线程数的线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixThreadPoolTest {
public static void main(String[] args) {
//创建固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
//使用线程池执行任务
for(int i=0;i<5;i++){
executorService.submit(new Runnable() {
@Override
public void run() {
for(int j=0;j<3;j++){
System.out.println(Thread.currentThread().getName() + " : " + j);
}
}
});
}

executorService.shutdown();
}
}

输出如下:

pool-1-thread-1 : 0

pool-1-thread-2 : 0

pool-1-thread-2 : 1

pool-1-thread-2 : 2

pool-1-thread-1 : 1

pool-1-thread-3 : 0

pool-1-thread-3 : 1

pool-1-thread-3 : 2

pool-1-thread-2 : 0

pool-1-thread-2 : 1

pool-1-thread-1 : 2

pool-1-thread-2 : 2

pool-1-thread-3 : 0

pool-1-thread-3 : 1

pool-1-thread-3 : 2

2.5.2 线程池二:单线程池 ​​SingleThreadPoolExecutor​

单线程串行执行任务,确保任务按提交顺序执行;

当线程异常结束后,会有新的线程代替之前的线程。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExecutor {
public static void main(String[] args) {
ExecutorService es = Executors.newSingleThreadExecutor();
for(int i=0;i<3;i++){
es.submit(new Runnable() {
@Override
public void run() {
for(int j=0;j<3;j++){
System.out.println(Thread.currentThread().getName() + " : " + j);
}
}
});
}
es.shutdown();
}
}

输出如下:

pool-1-thread-1 : 0

pool-1-thread-1 : 1

pool-1-thread-1 : 2

pool-1-thread-1 : 0

pool-1-thread-1 : 1

pool-1-thread-1 : 2

pool-1-thread-1 : 0

pool-1-thread-1 : 1

pool-1-thread-1 : 2

2.5.3 线程池三:缓存线程池 ​​CachedThreadPool()​

线程池数量不固定,可以达到最大值。

线程可被重复利用和回收。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadExecutor {
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<5;i++){
es.submit(new Runnable() {
@Override
public void run() {
for(int j=0;j<3;j++){
System.out.println(Thread.currentThread().getName() + " : " + j);
}
}
});
}
es.shutdown();
}
}

输出如下:

pool-1-thread-2 : 0

pool-1-thread-2 : 1

pool-1-thread-2 : 2

pool-1-thread-5 : 0

pool-1-thread-5 : 1

pool-1-thread-5 : 2

pool-1-thread-4 : 0

pool-1-thread-4 : 1

pool-1-thread-4 : 2

pool-1-thread-3 : 0

pool-1-thread-3 : 1

pool-1-thread-1 : 0

pool-1-thread-1 : 1

pool-1-thread-1 : 2

pool-1-thread-3 : 2

2.5.4 线程池四:周期性的线程池 ​​newScheduledThreadPool()​

创建一个周期性的线程池,支持定时及周期性执行任务

创建线程时,指定核心线程数,当执行任务较多超过核心线程时,可额外启动新的线程;

当任务恢复后,仅保留核心线程,其它额外线程将被关闭。

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadTest {
public static void main(String[] args) {
ScheduledExecutorService ses = Executors.newScheduledThreadPool(5);
ses.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行 " + new Date());
}
}, 5, 3, TimeUnit.SECONDS);
}
}

输出如下:

pool-1-thread-1 执行 Sun Sep 19 00:58:55 CST 2021

pool-1-thread-1 执行 Sun Sep 19 00:58:58 CST 2021

pool-1-thread-2 执行 Sun Sep 19 00:59:01 CST 2021

pool-1-thread-1 执行 Sun Sep 19 00:59:04 CST 2021

pool-1-thread-3 执行 Sun Sep 19 00:59:07 CST 2021

pool-1-thread-3 执行 Sun Sep 19 00:59:10 CST 2021

2.5.5 线程池五:新的线程池类 ​​ForkJoinPool​​ 的扩展 ​​newWorkStealingPool​

JDK1.8增加, 任务窃取线程池,线程有属于自己的队列,更加适用于多核心处理器。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class WorkStealingPoolTest {
public static void main(String[] args) {
ExecutorService es = Executors.newWorkStealingPool();
for(int i=0;i<10;i++){
es.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---END----");
}
}

输出如下:

ForkJoinPool-1-worker-1

ForkJoinPool-1-worker-1

ForkJoinPool-1-worker-1

ForkJoinPool-1-worker-2

ForkJoinPool-1-worker-3

ForkJoinPool-1-worker-3

ForkJoinPool-1-worker-3

ForkJoinPool-1-worker-3

ForkJoinPool-1-worker-2

ForkJoinPool-1-worker-1

---END----