当前位置:网站首页>多线程基础:线程基本概念与线程的创建
多线程基础:线程基本概念与线程的创建
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();
}
}
边栏推荐
- 美庐生物IPO被终止:年营收3.85亿 陈林为实控人
- AcWing 3537. Tree lookup complete binary tree
- Huawei 0 foundation - image sorting
- Test 1234
- Alibaba cloud international ECS cannot log in to the pagoda panel console
- Handwritten online chat system (principle part 1)
- 用友OA漏洞学习——NCFindWeb 目录遍历漏洞
- celery最佳实践
- Unity资源顺序加载的一个方法
- Xu Xiang's wife Ying Ying responded to the "stock review": she wrote it!
猜你喜欢
人体骨骼点检测:自顶向下(部分理论)
AUTOCAD——中心线绘制、CAD默认线宽是多少?可以修改吗?
用于远程医疗的无创、无袖带血压测量【翻译】
Deep circulation network long-term blood pressure prediction [translation]
Xu Xiang's wife Ying Ying responded to the "stock review": she wrote it!
This article discusses the memory layout of objects in the JVM, as well as the principle and application of memory alignment and compression pointer
SQL injection - access injection, access offset injection
美庐生物IPO被终止:年营收3.85亿 陈林为实控人
Describe the process of key exchange
Self-supervised Heterogeneous Graph Neural Network with Co-contrastive Learning 论文阅读
随机推荐
用友OA漏洞学习——NCFindWeb 目录遍历漏洞
深度循环网络长期血压预测【翻译】
Understanding disentangling in β- VAE paper reading notes
Crawling data encounters single point login problem
Huawei 0 foundation - image sorting
涂鸦智能在香港双重主板上市:市值112亿港元 年营收3亿美元
Jdbc driver, c3p0, druid and jdbctemplate dependent jar packages
How are you in the first half of the year occupied by the epidemic| Mid 2022 summary
AvL树的实现
Atcoder a mountaineer
Echart simple component packaging
图之广度优先遍历
十、进程管理
Implementation of AVL tree
Cocos2d Lua 越来越小样本 内存游戏
AcWing 3537. Tree lookup complete binary tree
二叉搜索树
视频化全链路智能上云?一文详解什么是阿里云视频云「智能媒体生产」
Alibaba cloud international ECS cannot log in to the pagoda panel console
Unity资源顺序加载的一个方法