首页 > 编程语言 >并发编程(一)之线程的创建和启动

并发编程(一)之线程的创建和启动

时间:2022-12-19 14:03:12浏览次数:54  
标签:Runnable run Thread start 编程 并发 线程 public


并发编程之线程的创建和启动

一、线程创建

1.1. 实现​​Runnable​​接口

实现​​Runnable​​​接口,重写​​run​​​方法,实现​​Runnable​​​接口的实现类的实例对象作为​​Thread​​​构造函数的​​target​​:

public class CreateThread implements Runnable {

@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ": 通过Runnable创建线程......");
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Thread createThread = new Thread(new CreateThread(), "子线程");
createThread.start();
System.out.println(Thread.currentThread().getName() + "主线程......");
}

}

我们看一下​​Runnable​​接口:

@FunctionalInterface
public interface Runnable {

public abstract void run();

}

我们可以看到​​Runnable​​​被​​FunctionInterface​​​接口,说明使用​​lamdba​​的写法去实现线程。很简洁。

public class CreateThread {

public static void main(String[] args) {

System.out.println(Thread.currentThread().getName() + "主线程......");

new Thread(() -> {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ": 通过Runnable创建线程......");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "子线程1").start();
}

}
1.2. 继承​​Thread​​类

继承​​Thread​​​类,重写​​run​​​方法。其实​​Thread​​​也是实现了​​Runnable​​​接口,里面有很多​​native​​方法。后面会分析。

public class CreateThread1 extends Thread {

@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ": 通过Runnable创建线程......");
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Thread createThread = new CreateThread1();
createThread.setName("子线程");
createThread.start();
System.out.println(Thread.currentThread().getName() + "主线程......");
}

}

我们简单看见一下​​Thread​​​里面的​​run​​方法:

@Override
public void run() {
if (target != null) {
target.run();
}
}

这个里面的​​target​​​其实就是我们传入的​​Runnable​​​,这也是为啥我们可以实现​​Runnable​​​接口的​​run​​​方法,这也是就是继承​​Thread​​​(把​​run​​​重写)和实现​​Runnable​​​(调用​​target.run()​​方法)的区别。

更值得我们注意的是​​run​​方法是异常的处理和抛出的,这意味的子线程发生异常,主线程是无法捕获到的(但是具体还是有处理的方法的,日后介绍,挖个坑,日后填)。

1.3. 总结
  1. 实现​​Runnable​​接口更好
  2. ​Runnable​​方便配合线程池使用
  3. ​Thread​​线程执行和线程创建无法解耦
  4. ​Thread​​继承之后无法继承其他线程,限制扩展性

最后再说一下 :创建线程我们可以有两种方法实现​​Runnable​​​和继承​​Thread​​​,但是本质来说创建线程只有一种(构造​​Thread​​​类),​​run​​​方法有两种传入​​Runnable​​​通过​​target.run()​​​调用和重写​​run()​​方法。

二、线程的启动

我们从上面的看到,线程的启动涉及到​​start()​​​和​​run()​​两种方法。

我们先看看​​start()​​方法:

/**
* 1. start方法将导致当前线程开始执行。由JVM调用当前线程的run方法
* 2. 结果是两个线程同时运行:当前线程(从对start方法的调用返回)和另一个线程(执行其run方法)
* 3. 不允许多次启动线程, 线程一旦完成执行就不会重新启动
*/
public synchronized void start() {
/**
* 对于VM创建/设置的主方法线程或“系统”组线程,不调用此方法。
* 将来添加到此方法的任何新功能可能也必须添加到VM中.
*
* threadStatus = 0 意味着线程处于初始状态
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();

/* 通知组此线程即将启动,以便可以将其添加到组的线程列表中,并且可以减少组的未启动计数。*/
group.add(this);

boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* 不做处理。如果start0抛出了一个Throwable,那么它将被传递到调用堆栈上 */
}
}
}

private native void start0();

从上面代码的注释我们可以看到:

  1. ​start()​​​方法被​​synchronized​​进行修饰,为同步方法,这样避免了多次调用问题;
  2. 使用​​threadStatus​​​(此变量被​​volatile​​修饰)记录线程状态;多次调用会抛出异常;
  3. 这方法会重写的​​run​​​方法被虚拟机调用,是子线程执行的​​run​​方法。

上面已经介绍了​​start​​​和​​run​​方法,接着我们写一个例子看看两个的区别:

public static void main(String[] args) {

Thread one = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "启动");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程1");

Thread two = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "启动");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程2");
one.start();
two.run();
System.out.println("主线程启动");

}

执行结果也很明显,调用​​run​​​会阻塞主线程,​​start​​是异步的。


标签:Runnable,run,Thread,start,编程,并发,线程,public
From: https://blog.51cto.com/luckyqilin/5952255

相关文章

  • 死磕Java多线程之线程池的基本使用与核心原理
    Java多线程之线程池一、线程池的自我介绍1.线程池的重要性(为什么使用线程池)线程池可以应对突然大爆发量的访问,通过有限个固定线程为大量的操作服务,减少创建和销毁线程所需......
  • C#多线程(四)并行编程篇之结构化
    C#多线程(四)并行编程篇之结构化 前言在前三章中我们的案例大量使用到了Thread这个类,通过其原始API,对其进行创建、启动、中断、中断、终止、取消以及异常处理,这样的写......
  • PLC 状态机编程第七篇 - 仿真运行
    我们就以星三角为例,演示仿真运行。。假设你已完成状态机,保存路径选择如下:点击Models标签,然后点击ModelSettings,进入ConfigurationParameters界面。点击Solv......
  • springboot实现AOP切面编程
    概述AOP(AspectOrientedProgramming)即面向切面编程。面向切面是面向对象中的一种方式而已。在代码执行过程中,动态嵌入其他代码,叫做面向切面编程(将交叉业务逻辑封装成成......
  • 探究:普通人都是怎么入门编程
    目录前景提要研究思考1.术语才是编程入门的领路人2.马上着手胜于做个目标,写个计划3.动起手来,比用眼睛看更有作用4.开发语言林立,你的武道何去何从总结前景提要很多人......
  • golang---进程、线程、goroutine
    创建进程os包及其子包os/exec提供了创建进程的方法。一般的,应该优先使用os/exec包。因为os/exec包依赖os包中关键创建进程的API,为了便于理解,我们先探讨os包中和......
  • python多线程实现爬虫任务
    python语言对于网络爬虫来说是非常重要的,大多数互联网公司都热衷于python语言编写爬虫。那么如果大批量做爬虫工作,如何才能快速的爬取数据,这就需要多线程多任务操作才能快速......
  • delphi D11编程语言手册 学习笔记(P424-477) 泛型
      这本书可以在Delphi研习社②群256456744的群文件里找到.书名:Delphi11AlexandriaEdition.pdf 泛型在C++中叫做类型模板(templateclasses),单从字面上理......
  • [编程基础] Python字符串替换笔记
    Python字符串替换笔记Python字符串替换笔记主要展示了如何在Python中替换字符串。Python中有以下几种替换字符串的方法,本文主要介绍前三种。replace方法(常用)translate......
  • Java实现异步编程的8种方式
    一、......