作为一个Java小白,持续学习是不可避免的,近期在学习多线程的一些知识,所以准备写几篇关于多线程的东西,做个知识记录,防止知识遗忘(头发渐少,大脑健忘)了。
多线程的知识和牵扯到的东西很多,此篇文章只是做一个简单的概述,剩下的后续会补充。
目录
3 实现Callable接口通过FutureTask包装器来创建Thread线程
1 进程与线程
进程与线程的关系:进程就像是工厂,线程就像是工厂中的生产线,进程(工厂)本身没有具体运行的属性,是线程(生产线)的容器,而进程是系统中基本的资源分配单位,也是基本的执行单位。
在中文维基百科中,是这样定义进程与线程的:
进程(英语:process),是指计算机中已运行的程序。进程为曾经是分时系统的基本运作单位。在面向进程设计的系统(如早期的UNIX,Linux 2.4及更早的版本)中,进程是程序的基本执行实体;在面向线程设计的系统(如当代多数操作系统、Linux 2.6及更新的版本)中,进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。若干进程有可能与同一个程序相关系,且每个进程皆可以同步(循序)或异步(平行)的方式独立运行。现代计算机系统可在同一段时间内以进程的形式将多个程序加载到存储器中,并借由时间共享(或称时分复用),以在一个处理器上表现出同时(平行性)运行的感觉。同样的,使用多线程技术(多线程即每一个线程都代表一个进程内的一个独立执行上下文)的操作系统或计算机体系结构,同样程序的平行线程,可在多CPU主机或网络上真正同时运行(在不同的CPU上)。
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如Windows 7的线程,进行混合调度。
同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。
一个进程可以有很多线程,每条线程并行执行不同的任务。
在多核或多CPU,或支持Hyper-threading的CPU上使用多线程程序设计的好处是显而易见,即提高了程序的执行吞吐率。在单CPU单核的计算机上,使用多线程技术,也可以把进程中负责I/O处理、人机交互而常被阻塞的部分与密集计算的部分分开来执行,编写专门的workhorse线程执行密集计算,从而提高了程序的执行效率。
在我们的电脑上,也可以直观的观察到线程与进程,打开我们的任务管理器。
任务管理器中的这三条分别是谷歌,TIM与微信程序的进程,我在TIM中打开两个聊天窗口,就会看见TIM的线程中有我打开的两个聊天窗口的线程,如下图
2 使用多线程
有三种方式创建多线程:
1 继承Thread类
代码如下:
package com.example.demo;
/**
* @Author: Java小学森
* @Date: 2019/3/7 2:05
* @Description:
**/
public class testThread extends Thread{
private String threadNm;
public testThread(String threadNm) {
this.threadNm = threadNm;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(threadNm + "线程显示:" + i);
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Main {
public static void main(String[] args) {
testThread thread1 = new testThread("A");
testThread thread2 = new testThread("B");
thread1.start();
thread2.start();
}
}
2 实现Runnable接口
代码如下:
package com.example.demo;
import static java.lang.Thread.sleep;
/**
* @Author: Java小学森
* @Date: 2019/3/7 2:05
* @Description:
**/
public class testThread implements Runnable {
private String threadNm;
public testThread(String threadNm) {
this.threadNm = threadNm;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(threadNm + "线程显示:" + i);
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Main {
public static void main(String[] args) {
testThread thread1 = new testThread("A");
testThread thread2 = new testThread("B");
new Thread(thread1).start();
new Thread(thread2).start();
}
}
3 实现Callable接口通过FutureTask包装器来创建Thread线程
代码如下:
package com.example.demo;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import static java.lang.Thread.sleep;
/**
* @Author: Java小学森
* @Date: 2019/3/7 2:05
* @Description:
**/
public class testThread implements Callable {
private String threadNm;
public testThread(String threadNm) {
this.threadNm = threadNm;
}
public Object call() {
for (int i = 0; i < 5; i++) {
System.out.println(threadNm + "线程显示:" + i);
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
}
class Main {
public static void main(String[] args) {
Callable callableA = new testThread("A");
Callable callableB = new testThread("B");
FutureTask futureTaskA = new FutureTask(callableA);
FutureTask futureTaskB = new FutureTask(callableB);
Thread threadA = new Thread(futureTaskA);
Thread threadB = new Thread(futureTaskB);
threadA.start();
threadB.start();
}
}
3 线程生命周期
线程的生命周期很复杂,结合别人的图我自己总结了一个流程图,有不对的地方请大家多多指正。其中的状态转化,以后会细说,这篇只是做一个简单概述。
4 线程状态
线程的状态在java中文api中分为了六类:分别是NEW,RUNNABLE,
BLOCKED
,
WAITING
,
TIMED_WAITING
,
TERMINATED,六类都可以在线程生命周期这一部分找到对应的位置。
文档中对此的解释为:
线程状态。线程可以处于下列状态之一:
NEW
至今尚未启动的线程处于这种状态。RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态。WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。TERMINATED
已退出的线程处于这种状态。
在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。
5 线程调度
线程的调度可以分为抢占式与非抢占式(协同式)调度。他们的区别是:
1:抢占式调度:每个线程可以获得CPU的资源不是由线程决定的,而是系统决定。
2:非抢占式(协同式):每个线程可以决定获得CPU资源的时间,执行时间由线程自己说了算。
Java用的是抢占式调度。
最后:南征北战,祝自己顺利!