当前位置:网站首页>多线程基础:线程基本概念与线程的创建
多线程基础:线程基本概念与线程的创建
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();
}
}
边栏推荐
- RedisSystemException:WRONGTYPE Operation against a key holding the wrong kind of value
- 一种用于夜间和无袖测量血压手臂可穿戴设备【翻译】
- Huawei 0 foundation - image sorting
- Picture zoom Center
- 具体说明 Flume介绍、安装和配置
- 随着MapReduce job实现去加重,多种输出文件夹
- Penetration test information collection - App information
- Example of implementing web server with stm32+enc28j60+uip protocol stack
- Splay
- 基于蝴蝶种类识别
猜你喜欢

十、进程管理

How are you in the first half of the year occupied by the epidemic| Mid 2022 summary

使用cpolar建立一个商业网站(1)

Introduction to the use of SAP Fiori application index tool and SAP Fiori tools

Splay
![A wearable arm device for night and sleeveless blood pressure measurement [translation]](/img/fd/947a38742ab1c4009ec6aa7405a573.png)
A wearable arm device for night and sleeveless blood pressure measurement [translation]

Implementation of AVL tree

图之广度优先遍历
![[the 300th weekly match of leetcode]](/img/a7/16b491656863e2c423ff657ac6e9c5.png)
[the 300th weekly match of leetcode]

爬虫玩得好,牢饭吃到饱?这3条底线千万不能碰!
随机推荐
用友OA漏洞学习——NCFindWeb 目录遍历漏洞
About NPM install error 1
MySQL查询请求的执行过程——底层原理
Estimate blood pressure according to PPG using spectral spectrum time depth neural network [turn]
Stm32+esp8266+mqtt protocol connects onenet IOT platform
Cocos2d Lua smaller and smaller sample memory game
STM32+ENC28J60+UIP协议栈实现WEB服务器示例
Installation and management procedures
C#/VB. Net to add text / image watermarks to PDF documents
基于蝴蝶种类识别
深度循环网络长期血压预测【翻译】
10、 Process management
AvL树的实现
线代笔记....
Hongke shares | plate by plate ar application in Beijing Winter Olympics
使用block实现两个页面之间的传统价值观
图之广度优先遍历
Atcoder a mountaineer
The role of applet in industrial Internet
Xu Xiang's wife Ying Ying responded to the "stock review": she wrote it!