首页 > 编程语言 >Java多线程01——多线程的创建

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

时间:2023-02-04 23:01:09浏览次数:43  
标签:01 Java Thread thread util running 线程 多线程 public

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----


标签:01,Java,Thread,thread,util,running,线程,多线程,public
From: https://blog.51cto.com/u_113754/6037308

相关文章

  • java基础:数组
    概述数组是相同类型数据的有序集合可以是任何类型每一个数据被称为该数组的一个数组元素,可以使用下标访问每一个元素下标从0开始,按顺序递增数组长度是固定的,创建后不......
  • 《分布式技术原理与算法解析》学习笔记Day01
    开篇词|四纵四横,带你透彻理解分布式技术谁更好掌握了分布式技术,谁就更容易在新一轮技术浪潮中获得主动。很多有多年工作经验的人,在分布式上面,也可能会有下面的问题:各......
  • PTA 1016 phone bills(把复杂的信息进行打包分类,把对象挑出来单个击破就容易解决的啦。
    #include<stdio.h>#include<stdlib.h>#include<string.h>structrecord{charname[25],time[15],flag[10];inttollTime,onOff;};structcustomers{i......
  • java基础:方法
    方法方法是解决一类问题的步骤的有序组合包含于类/对象中设计原则方法的原子性:一个方法只实现一个功能定义与调用方法的组成:方法的调用若方法返回值为空......
  • Java instanceof运算符
    javainstanceof运算符用于测试指定对象是否是指定类型(类或子类或接口)的实例。java中的instanceof也称为类型比较运算符,因为它将类型与实例进行比较。它返回true或fal......
  • Java静态绑定和动态绑定
    将方法调用连接到方法体称为绑定。在java中有两种类型的绑定:静态绑定(也称为早期绑定)。动态绑定(也称为后期绑定)。了解类型下面让我们来了解实例的类型。1.变......
  • Android集成mupdf,实现手写笔签字,手指翻页的java代码
    importandroid.graphics.Bitmap;importandroid.graphics.Color;importandroid.graphics.RectF;importandroid.util.Log;importjava.util.LinkedList;importc......
  • Java多态
    Java中的多态是一个概念,通过它我们可以通过不同的方式执行单个动作(方法)。多态性派生自2个希腊词:“poly”和“morphs”。词语“poly”意为许多,“morphs”意为形式。所......
  • 联想 Thinkpad Yoga 12 Gen 2 (2015) 电脑 Hackintosh 黑苹果efi引导文件
    原文来源于黑果魏叔官网,转载需注明出处。资源下载见评论区硬件型号驱动情况主板联想ThinkpadYoga12Gen2(2015)处理器英特尔酷睿i7-5600U已驱动内存8GB(智典DDR4......
  • Java final关键字
    java中的final关键字用于限制用户修改变量或重写一个类或方法。javafinal关键字可以在许多上下文中使用。final可以是:变量方法类final关键字可以应用于变量,没有......