线程运行状态
不管是多进程还是多线程实质上都不太可能一直进行 CPU 资源的占用,那么针对多线程的几种状态要掌握。
所有的系统资源是有限的,不管是多线程还是多进程,都必须在执行一段时间之后,让出资源,交由其他的线程继续执行,这是并发编程的本质。
1、创建过程:开发者定义好了相应的线程类对象,每一个 Thread 类的实例都表示一个线程对象;
2、就绪状态:多线程的启动依靠 start() 方法,当调用start()方法后所有的线程并不是立即执行,而是将进入到一个等待状态,等待 CPU 进行调度;
3、执行状态:当 CPU 执行调度到某个线程对象之后,该线程对象开始执行 run() 方法或 call() 但是并不意味一执行就将持续占用资源,而是在一段时间之后(一个时间片的时间之后),该线程就暂停执行;
4、阻塞状态:当某一个线程不在执行时(中断、休眠或调度失效),那么所有的线程将进入阻塞状态,如果此时线程没有执行完毕,则由阻塞状态切换到就绪状态,重新等待 CPU 的执行调度;
5、终止状态:如果多线程的执行代码体执行完毕或线程被强制性的结束,那么就将进入终止状态,将不会在进入就绪状态,即:该线程对象将不会被继续执行。
Thread类控制多线程:
通过之前分析应该对于多线程的类和接口有一个核心的认识,Runnable 和 Callable 主要定义线程的核心内容,而 Thread实现线程的控制,所以Thread 类中除了有启动多线程的方法外还有若干个控制操作。
线程命名与获取
多线程的运行状态是不固定的,所以针对所有的线程对象而言,就只能够通过名称来进行线程的唯一标记。供有如下的几个方法可以实现线程名称的操作。
| NO | 方法名称 | 类型 | 描述 |
| 1 | public Thread(Runnable target, String name) |
构造 | 接受Runnable及线程的名字 |
| 2 | public final void setName(String name) | 方法 | 设置 & 修改名字 |
| 3 | public final void getName(String name) | 方法 | 获取线程名字 |
由于所有的线程状态都是不可控的,所以唯一可以获取的只能时当前地线程对象,此时可以使用Thread 类中提供的线程方法:
public static Thread currentThread()
范例:观察线程的命名与获取:
import java.util.concurrent.Callable; class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程运行。。。。。"); } } public class MyBlog3 { public static void main(String[] args) { MyThread threadBody = new MyThread(); new Thread(threadBody).start(); new Thread(threadBody,"运行线程A").start(); new Thread(threadBody).start(); new Thread(threadBody,"运行线程B").start(); } }
运行结果:
运行线程B线程运行。。。。。
Thread-1线程运行。。。。。
Thread-0线程运行。。。。。
运行线程A线程运行。。。。。
可以发现,此时即使没有为线程设置名称,线程也会自动的生成一个名称。此名称实际上是采用线程创建顺序自动进行编号处理。
public Thread(ThreadGroup group, Runnable target)分配一个新的Thread对象。 此构造具有相同的效果Thread (group, target, gname)
,其中gname是新生成的名字。 自动生成的名称格式为"Thread-"+ n ,其中n为整数。
private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; }
通过源代码的分析可以发现,此时在 Thread 类中提供一个 static 的整型变量,这与之前分析的属性自动命名的结构是类似的。
观察如下代码:
import java.util.concurrent.Callable; class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程运行。。。。。"); } } public class MyBlog3 { public static void main(String[] args) { MyThread threadBody = new MyThread(); new Thread(threadBody,"自定义的的线程对象").start(); threadBody.run(); } }
运行结果:
自定义的的线程对象线程运行。。。。。
main线程运行。。。。。
其实所有的主方法的执行都是在 Java 程序里以主线程(main)的形式运行,所有的线程都是由进程创建的,那么进程在哪里?
每当用户使用 java 命令解释一个类的时候,实际就会自动启动一个 JVM 的进程。
范例:观察进程的存在:
public class Demo{ public static void main(String args[]){ String str = ""; for ( int x = 0; x < Integer.MAX_VALUE ; x++ ){ str += x ; } } }
所有的JVM 程序运行的时候都会默认启动一个新的进程,而现在的所有线程都是在此进程中产生的,在该进程执行的时候,会默认启动一个主线程,由主线程创建若干个子线程,所有的子线程并行执行。
线程休眠:
默认情况下线程对象只要启动了,那么就会持续的运行,一直到其运行完毕为止,但如果现在希望线程的执行速度可以缓慢一些,那么就可以对其进行休眠处理,在 Thread 类里提供了休眠的方法:
休眠:
public static void sleep(long millis) throws InterruptedException
-
public static void sleep(long millis, int nanos) throws InterruptedException
范例:观察休眠:
import java.util.concurrent.Callable; class MyThread implements Runnable{ @Override public void run() { for (int x = 0; x <20 ; x++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread .currentThread().getName()+"运行,x="+x); } } } public class MyBlog3 { public static void main(String[] args) { MyThread Body = new MyThread(); new Thread(Body,"运行线程").start(); Body.run(); } }
此时进入run()方法的线程对象可能有多个,但是会发现这多个线程对象感觉像一起休眠,一起自动唤醒。
未做任何处理的时候,所有的线程将同时进入run()方法之中,run()的代码也会按照先后的顺序依次执行,但由于多个线程之间的执行速度不一样,所以感觉像是同时执行。
线程中断
在之前使用休眠的时候会发现抛出一个"InterruptException",实际上就表示中断异常,也就是说所有的线程的执行都是可以被中断的。在Thread 类中提供有中断处理方法如下:
- public void interrupt() //中断线程。
- public boolean isInterrupted() //测试线程是否已经中断。线程的中断状态 不受该方法的影响。
范例:
public class MyBlog3 { public static void main(String[] args)throws Exception { Thread thread = new Thread(()->{ try { Thread.sleep(6000); System.out.println("吃饱喝足,开始学习"); } catch (InterruptedException e) { System.out.println("休息被中断了,开始玩"); } }); thread.start(); System.out.println(thread.getName()+"线程的中断状态:"+thread.isInterrupted()); Thread.sleep(1000); //先执行1秒 thread.interrupt(); //由主线程中断一个子线程 System.out.println(thread.getName()+"线程的中断状态:"+thread.isInterrupted()); } }
运行结果: Thread-0线程的中断状态:false 休息被中断了,开始玩 Thread-0线程的中断状态:true
线程中断的本质是:一个线程被另一个线程打断执行。线程一旦被中断,就会引发线程中断异常。
线程的强制执行
当程序中存在若干个线程的时候,那么这若干个线程之间彼此肯定是相互交替执行的,但若此时某个线程很紧急,需要优先处理完成,就可以采用线程的强制执行;
public final void join() throws InterruptedException //等待该线程终止。
public final void join(long millis) throws InterruptedException
//等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去
public final void join(long millis, int nanos)throws InterruptedException //等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
范例:观察线程的强制执行:
public class MyBlog3 { public static void main(String[] args) throws Exception { Thread mainThread = Thread.currentThread(); Thread thread = new Thread(() -> { for (int x = 0; x < 1000; x++) { if (x == 10) { try { mainThread.join(); //在第十个的时候主线程开始强制执行; } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "执行,x = " + x); } }, "循环线程"); thread.start(); for (int x = 0; x < 1000; x++) { Thread.sleep(1000); //延缓程序的执行速度 System.out.println(Thread.currentThread().getName() + "执行,x = " + x); } } }
执行结果(截选有代表性的一部分): .................................. 循环线程执行,x = 8 循环线程执行,x = 9 main执行,x = 0 main执行,x = 1 main执行,x = 2 main执行,x = 3 main执行,x = 4 main执行,x = 5 ...................................
使用 join() 会将当前的线程资源让出,交由其他线程执行,如果没有设置任何的时间单位,那么将等待其他线程执行完毕再恢复剩余线程的正常执行。
线程礼让
礼让的概念就是让出当前的执行操作,例如男生礼让女士座位。在两个线程进行资源处理的时候也可以实现这种谦让操作;
public static void yield() //暂停当前正在执行的线程对象,并执行其他线程。
范例:观察线程的礼让操作:public class MyBlog3 {
public static void main(String[] args) throws Exception {
Thread mainThread = Thread.currentThread();
Thread thread = new Thread(() -> {
for (int x = 0; x < 1000; x++) {
if (x % 3 == 0) {
Thread.yield(); // 礼让一次
System.out.println("【YIELD】线程礼让," + Thread.currentThread().getName());
}
try {
Thread.sleep(1000); //休眠一秒的时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行,x = " + x);
}
}, "循环线程");
thread.start();
for (int x = 0; x < 1000; x++) {
Thread.sleep(1000); //延缓程序的执行速度
System.out.println(Thread.currentThread().getName() + "执行,x = " + x);
}
}
}
执行结果(截取):
【YIELD】线程礼让,循环线程
main执行,x = 0
循环线程执行,x = 0
main执行,x = 1
循环线程执行,x = 1
循环线程执行,x = 2
main执行,x = 2
【YIELD】线程礼让,循环线程
循环线程执行,x = 3
main执行,x = 3
main执行,x = 4
循环线程执行,x = 4
main执行,x = 5
循环线程执行,x = 5
【YIELD】线程礼让,循环线程
main执行,x = 6
循环线程执行,x = 6
循环线程执行,x = 7
main执行,x = 7
main执行,x = 8
循环线程执行,x = 8
【YIELD】线程礼让,循环线程
循环线程执行,x = 9
main执行,x = 9
main执行,x = 10
循环线程执行,x = 10
main执行,x = 11
循环线程执行,x = 11
【YIELD】线程礼让,循环线程
main执行,x = 12
循环线程执行,x = 12
礼让指表示礼让一次,,随后就会继续按照资源抢占的模式来进行多线程的并发处理。
线程的优先级
理论上线程的优先级越高,越有可能先执行,在Thread类中为了方便优先级的管理定义有如下的优先级的操作方法:
| NO | 方法及常量 | 类型 | |
| 1 |
public final void setPriority(int newPriority) |
方法 | 设置线程的优先级 |
| 2 |
public final int getPriority |
方法 | 获取优先级 |
| 3 |
public static final int MIN_PRIORITY |
常量 | 最低优先级(数值为10) |
| 4 |
public static final int NORM_PRIORITY |
常量 | 中等优先级(5) |
| 5 |
public static final int MAX_PRIORITY |
常量 | 最高优先级(1) |
范例:线程优先级的设置:
public class MyBlog3{ public static void main(String[] args) throws Exception { Runnable run = ()->{ for (int x = 0; x < 100 ; x++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"运行,x = "+x); } }; Thread threadA = new Thread(run,"线程A"); Thread threadB = new Thread(run,"线程B"); Thread threadC = new Thread(run,"线程C"); threadA.setPriority(Thread.MIN_PRIORITY); threadB.setPriority(Thread.NORM_PRIORITY); threadC.setPriority(Thread.MAX_PRIORITY); threadA.start(); threadB.start(); threadC.start(); } }
运行结果(截选): 线程C运行,x = 0 线程B运行,x = 0 线程A运行,x = 0 线程C运行,x = 1 线程B运行,x = 1 线程A运行,x = 1 线程C运行,x = 2 线程B运行,x = 2 线程A运行,x = 2 线程A运行,x = 3 线程B运行,x = 3
优先级越高越有可能先进行调度,但是观察后发现并不是绝对的,同时也可以观察一个方法:
范例:主线程的优先级
public class MyBlog3{ public static void main(String[] args) throws Exception { System.out.println(Thread.currentThread().getPriority()); } }
运行结果:
5
通过结果可以发现用户自己创建的线程和主线程的优先级是相同的。
等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去
浙公网安备 33010602011771号