当前位置:网站首页>多线程基础:线程基本概念与线程的创建
多线程基础:线程基本概念与线程的创建
2022-07-06 11:01:00 【爪哇斗罗】
一. 线程与进程
1. 进程
一开始看线程与进程的概念可能会感觉到很抽象,晦涩难懂,先看下图:

上图是WIN10系统中的任务管理器,操作系统的.exe文件可以理解成一个进程,可以这样说,进程是操作系统运行的一个基本运行单元。
说到进程,有必要来说一下程序,程序由许多个指令与数据组成,就是指令序列。
这些指令需要运行,数据需要读写,指令加载至CPU,让CPU做指定的任务。
数据加载至内存,指令运行时,需要用到网络,磁盘等设备。这一过程就需要进程来加载指令,管理内存,处理IO的。
所以当一个程序运行时,磁盘加载代码至内存就相当于开启一个进程。比如当Java每编译一次.class文件。就会创建一个JVM进程。
我们可以将进程认为是程序运行的一个实例,并且大部分程序都可以运行多个进程实例,比如记事本,谷歌浏览器等,有的程序只能开一个线程,比如酷狗音乐等。
2. 线程
线程相当于进程中相互独立运行的子任务,这些子任务可以有多个, 它们共享着该进程所拥有的资源。
在Java中进程是资源分配的最小单位,线程作为最小的调度单位,先有进程,后有线程。
*二者对比总结*
1. 进程与进程之间是相互独立的,线程属于进程的一个子集存在于线程内。
2. 进程共享系统资源,如内存,网络端口供其线程使用。
3. 进程之间的通信较为复杂,本机通常使用IPC机制,不同计算机之间的通信通常使用Socket或者Http协议进行通信。
4. 线程虽然比进程轻,但是线程的上下文切换的成本非常高。
二. 并行与并发
2.1 并发
单任务运行环境下(单核CPU)线程执行方式是串行的,操作系统中的任务调度器,会将CPU的时间片分给不同程序去执行。
同一时间段内,线程轮流执行的方式叫做并发【concurrent】。换句话说就是一段时间内,处理多件事情的需求。
举个例子说明一下,什么是并发就会很好理解了:

在单核CPU下,现在有两个线程需要执行, 但是在某个时刻下只能让一个线程获得CPU资源去运行,假设当线程1开始运行到指令3时,必须依赖线程2去执行。
此时CPU会去执行线程2,而线程1必须等待线程2运行过后才可运行(大部分都是以时间片轮巡的方式)通过不断切换需要运行的线程的方式,这种就是并发【Parallel】:指两个或多个事件在同一时间段内发生。
这个过程运行时间很快,我们很难感觉到,我们感觉好像是两个线程同时运行的,总结为一句话就是: 微观串行,宏观并行。
2.2 并行
上面说了,并发是一段时间内,处理多件事情的需求。如何解决这种需求,通过并行,所以可以说并行是解决并发的一种方式。
关于并行的定义是这样:指两个或多个事件在同一时刻点同时发生。看下图:

现在,在多核CPU下,以上两个线程不需要等待某一方执行完毕后再去执行,而是可以同时让两个线程同时运行,这就是并行。
三. 同步与异步
同步与异步的概念相对来说比较简单,我们从方法的角度去理解:
假设有A与B两方法,调用A方法时必须返回调用后的结果才能执行B方法,这种就是同步。
相反,无需等待结果返回直接就可以执行B方法就是异步。
多线程是异步的,关于多线程异步调用的应用有很多举几个:
1. 较大文件的格式转换可以单开线程运行,防止阻塞。
2. tomcat中的异步Servlet,让用户线程去处理耗时任务,防止tomcat阻塞。
四. 创建线程
4.1 继承Thread
创建线程类之前,先看Thread类的构造:
public class Thread implements Runnable {}Thread类实现Runable接口,Runable接口中只有一个抽象方法:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}所以继承Thread类,需要重写其中的run()方法,将线程所处理的任务放到run()方法执行体中,然后通过创建该类的对象,执行start()方法,才是真正意义上的开启一个线程。
具体代码:
/**
* MyThread是个线程类
* @author 爪哇斗罗(javaDouluo)
*
*/
public class MyThread extends Thread {
/**
* 重写run方法
*/
@Override
public void run() {
System.out.println("MyRunThread===>" + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
// 开启线程
myThread.start();
// 当前线程名
System.out.println("main===>"+ Thread.currentThread().getName());
}
}运行结果:
![]()
注意:
上面有两个线程,一个是主线程,还有一个是我们自己创建的线程,不要认为线程的执行顺序是按照代码的放置顺序来执行的,这与你调用线程方法start()的放置顺序无关。
4.2 实现Runable接口
当出现线程类已经有一个父类时,继承Thread方法就不可行,可以使用实现Runable接口的方式来创建线程,就是对继承的一种拓展。
/**
* @author 爪哇斗罗(javaDouluo)
* @date 2022年06月25日 0:17
*/
public class MyRunable implements Runnable{
/**
* 实现Run方法
*/
@Override
public void run() {
System.out.println("MyRunThread===>" + Thread.currentThread().getName());
}
public static void main(String[] args) throws InterruptedException {
// 实例化当前线程对象
Runnable runnable = new MyRunable();
// 将当前线程对象入参至Thread
Thread thread = new Thread(runnable);
// 开启线程
thread.start();
// 当前线程名
System.out.println("main===>" + Thread.currentThread().getName());
}
}
这种方式的结果与上述是一样的。
4.3 FutureTask配合Thread
当线程结束之后我们需要返回一个结果可以使用FutureTask来接收一个Callable类型的参数来处理有返回结果的情况。
package com.jektong.thread.day01;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
/**
* @author 爪哇斗罗(javaDouluo)
* @date 2022年06月27日 23:10
*/
public class FutureTaskThread {
/**
* FutureTask通过配合Callable来实现有返回值得效果
* @param args
*/
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> integerFutureTask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("java");
TimeUnit.SECONDS.sleep(5);
return 0;
}
});
Thread thread = new Thread(integerFutureTask);
thread.start();
// 获取返回值
integerFutureTask.get();
}
}
边栏推荐
- None of the strongest kings in the monitoring industry!
- 关于npm install 报错问题 error 1
- 【中山大学】考研初试复试资料分享
- Introduction and case analysis of Prophet model
- Some recruitment markets in Shanghai refuse to recruit patients with covid-19 positive
- 2022-2024年CIFAR Azrieli全球学者名单公布,18位青年学者加入6个研究项目
- Medical image segmentation
- 同宇新材冲刺深交所:年营收9.47亿 张驰与苏世国为实控人
- 287. 寻找重复数
- Shangsilicon Valley JUC high concurrency programming learning notes (3) multi thread lock
猜你喜欢

CSRF漏洞分析

当保存参数使用结构体时必备的开发技巧方式

一种用于夜间和无袖测量血压手臂可穿戴设备【翻译】

None of the strongest kings in the monitoring industry!

2022-2024年CIFAR Azrieli全球学者名单公布,18位青年学者加入6个研究项目

Binary search tree

There is a sound prompt when inserting a USB flash disk under win10 system, but the drive letter is not displayed

Easy to use PDF to SVG program

Grafana 9.0 is officially released! It's the strongest!

Tree-LSTM的一些理解以及DGL代码实现
随机推荐
Markdown syntax for document editing (typera)
Atcoder a mountaineer
Binary search tree
徐翔妻子应莹回应“股评”:自己写的!
Unity资源顺序加载的一个方法
UFIDA OA vulnerability learning - ncfindweb directory traversal vulnerability
Self-supervised Heterogeneous Graph Neural Network with Co-contrastive Learning 论文阅读
裕太微冲刺科创板:拟募资13亿 华为与小米基金是股东
上海部分招工市场对新冠阳性康复者拒绝招录
当保存参数使用结构体时必备的开发技巧方式
【LeetCode第 300 场周赛】
Optical blood pressure estimation based on PPG and FFT neural network [translation]
Stm32+hc05 serial port Bluetooth design simple Bluetooth speaker
287. Find duplicates
关于npm install 报错问题 error 1
CSRF vulnerability analysis
Penetration test information collection - basic enterprise information
JDBC驱动器、C3P0、Druid和JDBCTemplate相关依赖jar包
上海部分招工市場對新冠陽性康複者拒絕招錄
10、 Process management