一、简介
程序:为了解决某个问题或执行某个操作编写一系列指令的集合program
进程:在操作系统中独立运行的程序,每运行一个应用程序就表示启动了一个进程 process
线程:是进程内部的一个执行单元,用来执行应用程序中的一个功能 thread
多线程:在一个应用程序中可以同时执行多个功能,例如迅雷中同时执行多个下载任务就是多线程
特性:
- 一个进程中可以包含多个线程,且至少要有一个线程
- 一个线程必须属于某个进程,进程是线程的容器
- 一个进程中的多个线程共享该进程的所有资源
二、进程与线程的区别
区别 | 进程 | 线程 |
---|---|---|
根本区别 | 作为资源分配的单位 | 调度和执行的单位 |
开销 | 每个进程都有独立的代码和数据空间,进程间的切换会有较大的开销 | 线程可以看做轻量级的进程同一线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,线程切换的开销小 |
所处环境 | 在操作系统中能运行多个任务(程序) | 在同一个应用程序中有多个顺序流同时执行 |
分配内存 | 系统在运行的时候会为每个进程分配不同的内存区域 | 除CPU外,不会为线程分配内存(线程所使的资源是它所属进程的资源)线程组只能共享资源 |
包含关系 | 没有线程的进程是可以看做成单线程的,如果一个进程中有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的。 | 线程是进程的一部分,所以线程有时候被称为轻权进程或轻量级进程 |
三、常见创建线程的方式
3.1 方式一,继承Thread类
第一步:定义一个类并继承Thread类,并重写run方法
public class MyThread extends Thread{
@Override
public void run() {
//代码编写区
for (int i = 0;i<5;i++){
System.out.print(i+" ");
}
System.out.println();
}
}
第二步:创建该类的实例,相当于创建了一个线程
public static void main(String[] args) {
//创建一个线程实例
MyThread myThread = new MyThread();
}
第三步:调用start()方法,即开始启动线程
public static void main(String[] args) {
//创建一个线程实例
MyThread myThread = new MyThread();
myThread.start();
}
3.2 方式二,实现Runable接口
第一步:定义一个类,去实现Runable接口,并重写run方法
public class MyRunable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
System.out.println();
}
}
第二步:创建刚定义那个类的实例
public static void main(String[] args) {
//创建MyRunable实例
MyRunable myRunable = new MyRunable();
}
第三步:创建一个Thread类实例,并把创建好的MyRunable对象传入该实例中
public static void main(String[] args) {
//创建MyRunable实例
MyRunable myRunable = new MyRunable();
//创建一个Thread实例,并把上面创建好的对象传入其中
Thread thread = new Thread(myRunable);
}
第四步:调用线程thread的start()方法,表示开始启动线程
public static void main(String[] args) {
//创建MyRunable实例
MyRunable myRunable = new MyRunable();
//创建一个Thread实例,并把上面创建好的对象传入其中
Thread thread = new Thread(myRunable);
//启动线程
thread.start();
}
3.3 两种方式的对比
继承Thread类 :
-
线程执行的代码放在Thread类的子类的run方法中
-
无法再继承其他类
买票案例演示:
public class Ticket extends Thread{ private int ticket = 5; @Override public void run() { for (int i = 0;i<ticket;i++){ System.out.print("已卖出第"+(i+1) + "张票" + ""); } } }
public static void main(String[] args) { new Ticket().start(); new Ticket().start(); new Ticket().start(); new Ticket().start(); }
运行代码后,会发现卖出了20张票,这表示每个线程都是独立的,并没有共享要卖的这5张票,这是不符合需求的
实现Runnable接口(推荐使用):
-
线程执行的代码放在Runnable接口的实现类的run方法中
-
可以继承其他类,避免单继承的局限性
-
适合多个相同程序代码的线程去处理同一个资源
-
增强程序的健壮性
卖票案例演示:
public class Ticket implements Runnable{ private int ticket = 5; @Override public void run() { for (int i = 0;i<100;i++){ if (ticket > 0){ System.out.println( "正在出售第" + (ticket--) + "张票"); } } } }
public static void main(String[] args) { Ticket ticket = new Ticket(); new Thread(ticket).start(); new Thread(ticket).start(); new Thread(ticket).start(); new Thread(ticket).start(); }
结果发现,不多也不少正好卖完了5张票,表示使用实现Runnable接口的方式是可以数据共享的
四、线程的生命周期
1.新生状态
用new关键字建立一个线程后,该线程对象就处亍新生状态。
处于新生状态的线程有自己的内存空间,通过调用start()方法进入就绪状态。
2.就绪状态
处于就绪状态线程具备了运行条件,但还没分配到CPU,处于线程就绪队列,等待系统为其分配CPU。
当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,该动作称为“CPU调度”。
3.运行状态
在运行状态的线程执行自己的run方法中代码,直到等待某资源而阻塞或完成任务而死亡。
如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态。
4.阻塞状态
处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己运行,进入阻塞状态。
在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续执行。
5.死亡状态
死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有三个,一个是正常运行的线程完成了它的全部工作;另一个是线程被强制性地终止,如通过stop方法来终止一个线程(不推荐使用);三是线程抛出未捕获的异常。
五、线程相关的的常用操作方法
方法名 | 方法描述 |
---|---|
public static Thread currentThread() | 返回目前正在执行的线程 |
public final String getName() | 获取线程的名称 |
public void setName(String name) | 设置线程的名称 |
public final int getPriority() | 获取线程的优先级别 |
public void setPriority(int newPriority) | 设置线程的优先级别 |
public final boolean isAlive() | 判断此此线程是否存活。 |
public final void join() | 调用该方法的线程,会让其它线程处于阻塞状态,该线程执行完毕后,其它线程再执行 |
public static void sleep(long millis) | 设置当前正在执行的线程休眠millis秒,线程处于阻塞状态 |
public static void yield() | 让当前正在执行的线程暂停一次,允许其他线程执行,不阻塞,线程进入就绪状态,如果没有其他等待执行的线程,这个时候当前线程就会马上恢复执行 |
public boolean isDaemon() | 测试此线程是否为守护线程 |
public void interrupt() | 中断此线程 |
5.1 currentThread() 返回目前正在执行的线程的信息
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread);
}
结果:Thread[main,5,main] //第一main表示线程的名称,5表示线程的优先级别,第二个main表示该线程所属的线程组
5.2 getName() 获取线程的名称
public class MyRunable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
MyRunable myRunable = new MyRunable();
System.out.println("线程名:" + new Thread(myRunable).getName());
Thread thread = Thread.currentThread();
System.out.println("线程名:" + thread.getName());
}
5.3 setName(String name) 设置线程的名称
public static void main(String[] args) {
MyRunable myRunable = new MyRunable();//实现了Runable接口
Thread thread = new Thread(myRunable);
System.out.println("原线程名:" + thread.getName());
thread.setName("th-001");
System.out.println("设置后的线程名:" + thread.getName());
}
5.4 getPriority() 获取线程的优先级别,在java中线程优先级分为1~10,数值越大,优先级越高,默认为5
public static void main(String[] args) {
MyRunable myRunable = new MyRunable();//实现了Runable接口
Thread thread = new Thread(myRunable);
System.out.println("此线程的优先级别为:" + thread.getPriority());
Thread main = Thread.currentThread();
System.out.println("main线程的优先级别为:" + main.getPriority());
}
5.5 setPriority(int newPriority) 设置线程的优先级
public static void main(String[] args) {
MyRunable myRunable = new MyRunable();//实现了Runable接口
Thread thread = new Thread(myRunable);
thread.setPriority(8);
System.out.println("此线程的优先级别为:" + thread.getPriority());
Thread main = Thread.currentThread();
System.out.println("main线程的优先级别为:" + main.getPriority());
}
5.6 isAlive() 判断线程是否存活,true表示还活着,false表示该线程已死亡
public static void main(String[] args) {
MyRunable myRunable = new MyRunable();
Thread thread =new Thread(myRunable);
System.out.println(thread.getName()+":" + thread.isAlive());
Thread main = Thread.currentThread();
System.out.println(main.getName() + ":"+main.isAlive());
}
5.7 join() 调用该方法的线程,会让其它线程处于阻塞状态,该线程执行完毕后,其它线程再执行
public class MyRunable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":" + i);
}
}
}
//没有线程调用join()方法之前
public static void main(String[] args) {
MyRunable myRunable = new MyRunable();
Thread thread = new Thread(myRunable);
thread.start();
for (int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":" + i);
}
}
public static void main(String[] args) throws InterruptedException {
MyRunable myRunable = new MyRunable();
Thread thread = new Thread(myRunable);
thread.start();
thread.join();//调用join方法
for (int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":" + i);
}
}
5.8 sleep(long millis) 设置当前正在执行的线程休眠millis秒,线程处于阻塞状态,单位为毫秒
public static void main(String[] args) throws InterruptedException {
for (int i = 0;i<5;i++){
if (i == 3){
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
System.out.println("睡眠了多久: "+(end-start)+"毫秒");
}
System.out.println(i);
}
}
5.9 yield() 让当前正在执行的线程暂停一次,允许其他线程执行,不阻塞,线程进入就绪状态,如果没有其他等待执行的线程,这个时候当前线程就会马上恢复执行
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 15; i++) {
System.out.println(this.getName()+":" + i);
if(i==7){
this.yield();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
myThread.start();
myThread2.start();
}
5.10 isDaemon() 测试此线程是否为守护线程
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyRunable());
boolean daemon = thread.isDaemon();
System.out.println(daemon);
System.out.println(Thread.currentThread().isDaemon());
}
5.11 interrupt() 中断此线程,线程的中断是由本身线程决定的,其它线程无法干预
public class MyRunable implements Runnable {
@Override
public void run() {
for (int i = 1;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
System.out.println("发现中断标记,结束线程");
return;
}
}
}
}
public static void main(String[] args) throws InterruptedException {
MyRunable myRunable = new MyRunable();
Thread myThread = new Thread(myRunable, "lisi");
myThread.start();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
Thread.sleep(1000);
}
myThread.interrupt();
}
}
}
}
}
public static void main(String[] args) throws InterruptedException {
MyRunable myRunable = new MyRunable();
Thread myThread = new Thread(myRunable, "lisi");
myThread.start();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
Thread.sleep(1000);
}
myThread.interrupt();
}