目录
一、优先级
二、线程的生命周期
三、线程中的常用方法
常用方法
注意事项
给出一道面试题
一、优先级
在Java程序中,一个程序通常会有多个线程同时进行来执行多个任务,使CPU的资源利用率得到最大化。但是我们知道,对于单核CPU来说多线程是通过在多个线程之间快速切换来实现所谓的“多线程同时进行”的。也就是说,我们多个线程并不是真正的同时执行,而是在交替执行,CPU在执行完一次任务后会释放资源来执行下一个任务,那么在选择下一次任务时能不能通过某种方法来使某个线程获得CPU的概率更大呢?答案就是给各个线程规定一个优先级,优先级高的线程较优先级低的线程更有可能获得下一次CPU执行权。
在Java中,线程的优先级从1到10分为十个优先级,数字越高代表优先级越高,越有可能获得下一次CPU执行权。同时Thread类中声明了三个静态变量来表示三个特殊优先级,分别是:
MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5
我们可以通过使用如下方法来获取一个线程的优先级
public final int getPriority()
也可以使用如下方法来设置一个线程的优先级
public final void setPriority(int newPriority)
例:创建一个线程thread1,修改其优先级为8,然后再创建一个新线程thread2,比较两个线程优先级
package t;
//通过继承Thread类的方法创建一个线程
public class ThreadTest1 extends Thread{
//重写run()方法
public void run() {
for(int i=1;i<100;i++) {
System.out.println("i="+i);
}
}
}
package t;
public class ThreadTest {
public static void main(String []args) {
//创建xianchengthread1
ThreadTest1 thread1=new ThreadTest1();
//设置其优先级为8
thread1.setPriority(8);
//创建线程thread2
ThreadTest1 thread2=new ThreadTest1();
//获取两个线程的优先级
int priority1=thread1.getPriority();
int priority2=thread2.getPriority();
//输出线程优先级之差
System.out.println(priority1-priority2);
//启动线程
thread1.start();
thread2.start();
}
}
二、线程的生命周期
就像人从出生到死亡分为婴儿、少年...老年、死亡等多个阶段一样,线程从创建到死亡也分为多个阶段,每个阶段有各自的特性。
线程分为创建、就绪、运行、阻塞、死亡五种状态
接下来分别介绍五种状态分别对应的情形
- 新建:当一个线程创建但没有执行start()方法时,称该线程处于创建状态。
- 就绪:新建的下一个状态,当调用了start()方法后等待CPU分配资源来运行线程的过程叫做就绪状态。
- 运行:当CPU分配资源给就绪状态的线程后,改线程就会进入运行状态。
- 阻塞:当一个线程调用某个方法后,会使自己进入一个无法获取CPU资源的状态,这个状态就是阻塞状态。
- 死亡:顾名思义,当一个线程任务结束后便会进入死亡状态。
在这几个状态中有如下注意点(涉及到上面没讲的方法见后面的常用方法部分)
- 能使线程进入阻塞状态的方法有:sleep(long millis)、join()、wait()。
- CPU运行完线程切换到下一个线程后,该线程处于就绪状态不是阻塞状态,要区分阻塞和就绪状态。
- 当将线程thread1作为参数创建新线程thread2调用thread2.start()后,thread1线程仍处于新建状态,但是线程thread2处于就绪(运行)状态。(这是因为调用的是thread2的start()方法)
- 处于死亡状态的线程没办法再使用。
三、线程中的常用方法
我们先给出常用的方法然后再给出相关注意事项
常用方法:
public synchronized void start():启动一个线程
public void run():需要重写的方法,方法体就是这个线程要干的事
public static native Thread currentThread():获取调用该代码的线程
public final String getName():获取线程的名字
public final synchronized void setName(String name):设置线程的名字
public static native void yield():释放CPU资源进入就绪状态
public final void join():在a线程中调用b线程的join()方法会使a线程进入阻塞状态直到b线程运行结束
public static native void sleep(long millis):使线程进入阻塞状态millis毫秒
public final native boolean isAlive():判断一个线程是否存活
public final int getPriority():获取一个线程的优先级
public final void setPriority(int newPriority):设置线程优先级为newPriority
public void wait():使线程进入阻塞状态,直到被别的线程调用notify()唤醒
public void notify():唤醒进入wait状态中优先级最高的线程
public void notifyAll():唤醒所有进入wait状态的线程
注意事项:
- 使用yield后不意味着接下来运行的一定不是该线程,它有可能刚释放CPU资源后 立马又获取了CPU资源 。
- 使用sleep方法使线程进入阻塞状态后,该线程并不会释放锁,也就是说其他线程也没办法使用该同步代码。
- run()方法没有提供throws来解决异常,所以sleep中的异常不能通过throws方法来解决。
- wait()和notify()方法是定义在Object类中的,不是定义在Thread类中的。
- wait状态线程被notify后会继续执行wait后面的代码。
- 如果所有线程都进入wait状态,程序不会报错也不会停止,要避免出现这种情况。
给出一道面试题
说说sleep和wait的异同
相同点:都会使一个线程进入阻塞状态
不同点:1.sleep()是定义在Thread类中的而wait()是定义在Object类中的。
2.sleep()不会释放同步锁而wait()会释放同步锁