本篇博客包含如下内容:
- 进程和线程的的概念
- 线程的创建和基本操作
- 多线程实例
- 线程的停止
词条引入:
多线程:
多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。[1] 在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程(台湾译作“执行绪”),进而提升整体处理性能。 ——百度百科
进程和线程的的概念
什么是进程?
- 是一个正在执行中的程序。每个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
- 从程序开发角度来讲,进程是资源分配的基本单位,是一个程序或者服务的基本单位。我们可以说进程就是程序的执行过程,这个过程包括很多东西,如CPU执行时间、运行内存、数据等,而且是一个动态的过程。进程是线程的载体,进程持有资源(共享内存,共享文件)和线程。抛去进程去谈论线程将没有意义。
什么是线程?
- 线程是指一个任务从头到尾的执行流。
- 线程是轻量级的进程,它们共享在父进程拥有的资源下,每个线程在父进程的环境中顺序的独立的执行一个活动,每个CPU核心在同一时刻只能执行一个线程,尽管我们有时感觉自己的计算机同时开着多个任务,其实他们每个的执行都是走走停停的,CPU轮流给每个进程及线程分配时间。
- 在单处理器系统中,多个线程共享CPU的时间称为时间共享,所有资源调动都由操作系统完成。
线程和进程的关系?
- 线程是系统中最小的执行单元(操作系统识别)
- 一个线程只能属于一个进程,同一个进程中有多个线程,但至少有一个线程
- 线程共享进程的资源(内存,共享代码,常量,全局变量及常量),但是每个线程拥有自己的栈段,也叫运行时段。存放所有局部变量和临时变量。
多个线程的关系?
- 线程之间需要进行通讯(线程交互)程序才能更好的执行
- 交互方式包括互斥和同步
多线程的意义!
- 多线程可以使程序反应更快,交互性更强,执行效率更高
线程的创建和基本操作
Java对线程的支持:
- Tread类(实现了Runnable接口)
- Thread类包括创建线程的构造方法和控制线程的一些方法
- Runnable接口
- 在Java中,每个任务都是Runnable接口的一个实例,称为可运行对象。线程本质上讲就是便于任务执行的对象。
API之线程的常见方法
Thread常用方法:
类别 | 方法 | 简介 |
---|---|---|
线程的创建 | Thread() | 创建一个空线程 |
…… | Thread(String name) | 创建线程并指定名称 |
…… | Thread(Runnable target) | 为指定任务创建一个线程 |
…… | Thread(Runnable target,String name) | 为指定任务创建线程并制定线程名 |
如何完成任务 | void run() | 线程执行的代码存放于此 |
线程的方法 | void start() | 启动线程,使run方法被JVM调用 |
…… | static void sleep(long millis) | 线程休眠指定毫秒数 |
…… | static void sleep(long millis,int nanos) | 线程休眠指定毫秒数加指定纳秒数 |
…… | void join() | 使其它线程等待当前线程的终止 |
…… | void join(long millis) | 等待当前线程终止,在指定毫秒内 |
…… | void join(long millis,int nanos) | …… |
…… | static void yield() | 暂停线程并允许执行其他线程 |
获取线程的引用 | static Thread currentThread() | 返回当前运行的线程引用 |
获取线程的标识符 | long getId() | 返回线程的标识符 |
获取线程的名称 | String getName() | 返回线程名称 |
测试线程 | Boolean isAlive() | 测试当前线程是否正在运行 |
优先级 | void setPriority(int p) | 设置线程的优先级,(1~10) |
那些被弃用的方法:
官方说明:
Why Are Thread.stop, Thread.suspend,
Thread.resume and Runtime.runFinalizersOnExit Deprecated?
方法 | 描述 | 弃用理由 |
---|---|---|
void stop() | 终止一个线程 | 天生的不安全,终止包括run方法在内的未结束的方法,使得对象处于不一致的状态 |
void suspend() | 用来阻塞一个线程直至另一个线程调用resume | 经验证明suspend经常导致死锁 |
void resume() | 只与 suspend() 一起使用 | suspend()都已被弃用,so |
以上为部分常用的的方法,了解更多详情请查阅JDK API
Runnable接口中的方法:
只有一个抽象方法:
public abstract void run()
为什么Thread类和Runnable接口中都有run方法?
- run方法指明如何完成该项任务,启动线程JVM会自动调用此方法。所以无需手动调用,如果手动调用只是执行了方法里的内容,但是并没有新线程被启动。
- Thread类实现Runnable接口,此处说明如果想要用实现Runnable接口这种方式,必须得借助Thread来实现。但为什么要这样设计呢?我们知道Java是没有多重继承的,所以如果我们采用继承Thread类的方式来实现多线程,那个这个类就不能在继承别的类了,这显然有些时候不太方便,所以我们很多时候可以采用实现Runnable接口。
为什么推荐使用Runnable接口来建立线程?
- 除了上面提到的拓展性和继承的局限性之外,还因为,Thread类创建线程时将任务与运行任务的机制(线程)混在了一起。而Runnable接口将任务从线程分离出来。
线程的创建和启动
- 继承Thread类,重写run方法,建立Thread子类对象(新建线程),并用其调用start()方法,start自动调用run方法中的线程代码执行。
- 实现Runnable接口,重写run方法,创建Runnable的实现类对象(新建任务),用Tread建立线程并将Runnable子类对象传入构造函数(新建线程),调用start
多线程实例
实例:
给出一个程序,它创建三个任务以及三个运行这些任务的线程。
- 第一个任务打印’a’ 100次
- 第二个打印’b’ 100次
- 第三个打印1-100的整数
实例代码:
package thread;
/**
* 打印字符指定次数
* @author StruggleYang
*/
public class RunnableImp1 implements Runnable {
private char printChar;
private int count;
public RunnableImp1(char printChar,int count) {
this.printChar = printChar;
this.count = count;
}
@Override
public void run() {
for (int i = 0; i < count; i++) {
System.out.print(printChar+",");
}
System.out.println();
}
}
package thread;
/**
* 打印1-x的整数
* @author StruggleYang
*/
public class RunnableImp2 implements Runnable {
private int printNum;
public RunnableImp2(int printNum) {
this.printNum = printNum;
}
@Override
public void run() {
for (int i = 0; i < printNum; i++) {
System.out.print(i+" ");
}
}
}
package thread;
/**
* 打印字符T,继承Thread类
* @author StruggleYang
*/
public class ThreadExt extends Thread {
private char printT;
public ThreadExt(char printT) {
super();
this.printT = printT;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print(printT);
}
}
}
package thread;
/**
* 线程测试类
* @author StruggleYang
*/
public class TreadTest {
public static void main(String[] args) {
// 创建Runnable接口的实现类实例,一个实例相当于一个任务
RunnableImp1 printChar1 = new RunnableImp1('a',100);
RunnableImp1 printChar2 = new RunnableImp1('b',100);
RunnableImp2 printNum = new RunnableImp2(100);
// 创建对应的线程
Thread t1 = new Thread(printChar1);
Thread t2 = new Thread(printChar2);
Thread t3 = new Thread(printNum);
Thread t4 = new ThreadExt('T');
// start启动线程,他会调用任务中的run方法并执行,run方法结束,线程结束。
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果(因为线程持有CPU执行权是不固定的,所以每次运行结果都不一样):
aaaaaaaaaaaaa0 a1 babbbbb2 bbTaTTTTb3……
线程的停止
- run方法结束,线程停止
- 使用interrupt(中断)方法。