李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
02.并发编程之线程的创建
Leefs
2022-10-03 PM
932℃
0条
[TOC] ### 前期准备 **(1)创建Maven项目** **(2)在`pom.xml`中引入如下依赖** ```xml
junit
junit
4.11
test
org.projectlombok
lombok
1.18.22
provided
org.slf4j
slf4j-api
1.7.22
ch.qos.logback
logback-classic
1.2.3
org.junit.jupiter
junit-jupiter
RELEASE
compile
``` **(3)在resources目录下创建`logback.xml`文件** ```xml
%date{HH:mm:ss.SSS} %c [%t] - %m%n
``` #### 一、直接使用Thread创建线程 **语法** ```java // 创建线程对象 Thread t = new Thread() { public void run() { // 要执行的任务 } }; // 启动线程 t.start(); ``` **示例** ```java import lombok.extern.slf4j.Slf4j; /** * Created by lilinchao * Date 2022/10/3 * Description 使用 Thread创建线程 */ @Slf4j(topic = "c.Test01") public class Test01 { public static void main(String[] args) { // 匿名内部类方式创建 Thread // 参数"t1":表示该线程名称 Thread t1 = new Thread("t1") { @Override // run 方法内实现了要执行的任务 public void run() { log.debug("t1 running"); } }; //启动线程 t1.start(); // t1.run(); //在main线程中输出日志到控制台 log.debug("main running"); } } ``` **运行结果** ``` 17:08:35.519 c.Test01 [main] - main running 17:08:35.519 c.Test01 [t1] - t1 running ``` Java 8 以后可以使用 lambda 精简代码 ```java Thread t1 = new Thread(()->{ log.debug("t1 running"); }, "t1"); //启动线程 t1.start(); ``` > start()和run()方法的区别 在使用Thread 创建线程时,不管使用start()方法还是使用run()方法,程序都可以正常运行起来,但是还是有本质区别的 + **start()**:作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用。 通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,run()方法运行结束,此线程随即终止。 + **run()**:和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程。 如果在主线程中直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径只有一条,程序执行时还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到多线程的目的。 *start()方法会启动一个新线程,并在新线程中运行run()方法。而run()则会直接在当前线程中运行该方法,并不会启动一个新线程来运行。* #### 二、使用 Runnable 配合 Thread创建线程(推荐) 把【线程】和【任务】(要执行的代码)分开 - Thread 代表线程 - Runnable 可运行的任务(线程要执行的代码) **语法** ```java Runnable runnable = new Runnable() { public void run(){ // 要执行的任务 } }; // 创建线程对象 Thread t = new Thread( runnable ); // 启动线程 t.start(); ``` **示例** ```java import lombok.extern.slf4j.Slf4j; /** * Created by lilinchao * Date 2022/10/3 * Description 使用 Runnable 配合 Thread */ @Slf4j(topic = "c.Test02") public class Test02 { public static void main(String[] args) { // 创建任务对象 Runnable task2 = new Runnable() { @Override public void run() { log.debug("t2 running"); } }; // 参数1 是任务对象; 参数2 是线程名称 Thread t2 = new Thread(task2, "t2"); t2.start(); log.debug("main running"); } } ``` **运行结果** ``` 17:54:19.618 c.Test01 [main] - main running 17:54:19.619 c.Test01 [t2] - running ``` **使用 lambda 精简代码** 因为 Runnable 接口标注了 `@FunctionalInterface` 这个注解,表示是一个函数式接口,可以使用 lambda 表达式 ```java // 创建任务对象 Runnable task2 = () -> log.debug("t2 running"); // 参数1 是任务对象; 参数2 是线程名称 Thread t2 = new Thread(task2, "t2"); t2.start(); ``` ##### Thread 与 Runnable 的关系 通过源码层面来说明Thread 与 Runnable 的关系 + **Runnable源码** ``` @FunctionalInterface public interface Runnable { public abstract void run(); } ``` + **Thread源码(部分)** ```java public class Thread implements Runnable { /* What will be run. */ private Runnable target; public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { //... this.target = target; //... } @Override public void run() { if (target != null) { target.run(); } } ``` **结论** - 在`Test01`类中是把线程和任务合并在了一起,`Test02`类中是把线程和任务分开了 - 用 Runnable 更容易与线程池等高级API 配合 - 用 Runnable 让任务类脱离了 Thread 继承体系,更灵活 #### 三、FutureTask 配合 Thread创建线程 `FutureTask` 能够接收 Callable 类型的参数,用来处理有返回结果的情况 ```java import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * Created by lilinchao * Date 2022/10/3 * Description FutureTask 配合 Thread */ @Slf4j(topic = "c.Test03") public class Test03 { public static void main(String[] args) throws ExecutionException, InterruptedException { // 创建任务对象 FutureTask
task3 = new FutureTask<>(() -> { log.debug("t3 running"); //执行完毕后返回结果 100 return 100; }); // 参数1 是任务对象; 参数2 是线程名称 new Thread(task3, "t3").start(); // 主线程阻塞,同步等待 task 执行完毕的结果 Integer result = task3.get(); //get()方法获取返回结果 log.debug("main running"); log.debug("结果是:{}", result); } } ``` **运行结果** ``` 18:23:39.883 c.Test03 [t3] - t3 running 18:23:39.886 c.Test03 [main] - main running 18:23:39.886 c.Test03 [main] - 结果是:100 ``` ##### 源码分析 + **FutureTask源码(部分)** ```java public class FutureTask
implements RunnableFuture
{ /** The underlying callable; nulled out after running */ private Callable
callable; /** The result to return or exception to throw from get() */ private Object outcome; // non-volatile, protected by state reads/writes public FutureTask(Callable
callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } public void run() { //... try { Callable
c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } //... } protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v; UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); } } public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); } private V report(int s) throws ExecutionException { Object x = outcome; if (s == NORMAL) return (V)x; if (s >= CANCELLED) throw new CancellationException(); throw new ExecutionException((Throwable)x); } } ``` + **Callable源码** ```java @FunctionalInterface public interface Callable
{ /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; } ``` **说明** - `FutureTask`内置了一个Callable对象,初始化方法将指定的Callable赋给这个对象。 - `FutureTask`实现了Runnable接口,并重写了Run方法,在Run方法中调用了Callable中的call方法,并将返回值赋值给outcome变量 - get方法就是取出outcome的值。
标签:
并发编程
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2429.html
上一篇
01.并发编程之进程与线程概述
下一篇
03.并发编程之线程的运行
评论已关闭
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
NLP
4
标签云
Kibana
并发线程
Thymeleaf
二叉树
JavaWeb
MyBatisX
Elasticsearch
链表
JavaWEB项目搭建
队列
DataWarehouse
前端
Golang基础
线程池
Tomcat
Netty
FastDFS
Golang
RSA加解密
Livy
pytorch
Spark RDD
Scala
机器学习
Docker
Linux
Java阻塞队列
设计模式
微服务
Eclipse
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭