1、线程状态
线程状态:6种
- NEW 新建状态
- RUNNABLE 可运行状态(就绪状态)
- RUNNING 运行状态
- BLOCKED 阻塞状态
- WAITING 等待状态
- TERMINATED 终止状态(死亡状态)
线程的状态转换示意图:
2、线程操作的相关方法
- getName() 获取线程名称 setName()是设置线程名称
- getId() 获取线程ID
- getPriority() 获取线程优先级, Java线程优先级10个等级,最小的是1,默认值是5,最大的是10,优先级越高被优先调用的概率最高
- run() 线程Thread的重要方法
- start() 创建一个线程,新线程操作执行体,调用run()
- sleep() 线程休息,
- join() 加入线程
- yield() 暂停当前线程,交出CPU的使用权,其他线程可被调度,可被调度的线程优先级要大于或等于当前线程,线程并不一定会停止执行,有可能再次获取CPU的调度
- wait() 等待状态,是当前线程等待,并让该线程释放对象锁,放进对象的等待池中,等待其他线程将其唤醒。
- notify() 通知方法,唤醒等待池中的线程,(随机选择一个唤醒)
- notifyAll() 通知方法,唤醒等待池中所有线程
class ThreadDemo implements Runnable {
public void run() {
for(int i = 1;i <= 5;i++) {
System.out.println("当前运行线程:"+Thread.currentThread().getName()
+",i="+i+",活动线程的数目:"+Thread.activeCount());
}
}
}
public class TestDemo6 {
/**
* 获取和设置线程名称
*/
private static void gain() {
System.out.println("获取和设置线程名称");
ThreadDemo tm = new ThreadDemo();
Thread t1 = new Thread(tm); //系统自动命名
Thread t2 = new Thread(tm,"线程-1"); //自己设置线程名称
Thread t3 = new Thread(tm);
t3.setName("线程-2"); //为t3设置线程名称
System.out.println("活动线程数目:"+Thread.activeCount()); //获取活动线程的数目
t1.start();
t2.start();
t3.start();
}
/**
* 设置线程优先级
*/
private static void install() {
System.out.println("===========================");
ThreadDemo tm = new ThreadDemo();
Thread t1 = new Thread(tm,"线程-1");
Thread t2 = new Thread(tm,"线程-2");
Thread t3 = new Thread(tm,"线程-3");
Thread t4 = new Thread(tm,"线程-4");
t1.setPriority(Thread.MIN_PRIORITY); //设置线程-1的优先级最低
t2.setPriority(2); //设置线程-2的优先级为2
t3.setPriority(Thread.NORM_PRIORITY); //设置线程-3的优先级为默认5
t4.setPriority(Thread.MAX_PRIORITY); //设置线程-4的优先级最高10
t1.start();
t2.start();
t3.start();
t4.start();
}
public static void main(String[] args) {
//gain();
install();
}
}
运行gain()方法:
获取和设置线程名称
活动线程数目:1
当前运行线程:Thread-0,i=1,活动线程的数目:3
当前运行线程:线程-1,i=1,活动线程的数目:3
当前运行线程:Thread-0,i=2,活动线程的数目:4
当前运行线程:线程-1,i=2,活动线程的数目:4
当前运行线程:Thread-0,i=3,活动线程的数目:4
当前运行线程:线程-1,i=3,活动线程的数目:4
当前运行线程:Thread-0,i=4,活动线程的数目:4
当前运行线程:Thread-0,i=5,活动线程的数目:4
当前运行线程:线程-1,i=4,活动线程的数目:3
当前运行线程:线程-1,i=5,活动线程的数目:3
当前运行线程:线程-2,i=1,活动线程的数目:3
当前运行线程:线程-2,i=2,活动线程的数目:2
当前运行线程:线程-2,i=3,活动线程的数目:2
当前运行线程:线程-2,i=4,活动线程的数目:2
当前运行线程:线程-2,i=5,活动线程的数目:2
运行install()方法:
===========================
当前运行线程:线程-3,i=1,活动线程的数目:4
当前运行线程:线程-1,i=1,活动线程的数目:5
当前运行线程:线程-3,i=2,活动线程的数目:5
当前运行线程:线程-4,i=1,活动线程的数目:5
当前运行线程:线程-3,i=3,活动线程的数目:5
当前运行线程:线程-2,i=1,活动线程的数目:5
当前运行线程:线程-1,i=2,活动线程的数目:5
当前运行线程:线程-2,i=2,活动线程的数目:5
当前运行线程:线程-3,i=4,活动线程的数目:5
当前运行线程:线程-4,i=2,活动线程的数目:5
当前运行线程:线程-3,i=5,活动线程的数目:5
当前运行线程:线程-2,i=3,活动线程的数目:5
当前运行线程:线程-2,i=4,活动线程的数目:4
当前运行线程:线程-2,i=5,活动线程的数目:4
当前运行线程:线程-1,i=3,活动线程的数目:5
当前运行线程:线程-4,i=3,活动线程的数目:5
当前运行线程:线程-4,i=4,活动线程的数目:3
当前运行线程:线程-4,i=5,活动线程的数目:3
当前运行线程:线程-1,i=4,活动线程的数目:3
当前运行线程:线程-1,i=5,活动线程的数目:2
线程休眠:
- 当线程休眠结束后,该线程并不会立即进入运行状态,而是进入就绪状态,等待JVM为他分配内存
- 当线程处于休眠状态,如果该线程被中断,则会抛出InterruptedException异常,中断线程可以使用Thread类提供的interrupt()方法
- sleep()是静态方法,在主方法内,无论是通过线程对象去调用sleep()还是直接通过Thread.sleep()形式去调用sleep()方法,都是休眠主线程。若想要线程实例休眠,sleep()方法应该放在run()方法内
class ThreadDemo1 implements Runnable {
public void run() {
for(int i = 1;i <= 5;i++) {
System.out.println("当前运行线程:"+Thread.currentThread().getName()
+",i="+i+",时间:"+(new Date()));
try {
Thread.sleep(1000); //线程休眠1s
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TestDemo7 {
public static void main(String[] args) {
ThreadDemo1 tm = new ThreadDemo1();
Thread t1 = new Thread(tm,"线程-1");
Thread t2 = new Thread(tm,"线程-2");
Thread t3 = new Thread(tm,"线程-3");
t1.start();
t2.start();
t3.start();
}
}
运行结果:
当前运行线程:线程-2,i=1,时间:Tue Aug 07 19:10:23 CST 2018
当前运行线程:线程-3,i=1,时间:Tue Aug 07 19:10:23 CST 2018
当前运行线程:线程-1,i=1,时间:Tue Aug 07 19:10:23 CST 2018
当前运行线程:线程-2,i=2,时间:Tue Aug 07 19:10:24 CST 2018
当前运行线程:线程-1,i=2,时间:Tue Aug 07 19:10:24 CST 2018
当前运行线程:线程-3,i=2,时间:Tue Aug 07 19:10:24 CST 2018
当前运行线程:线程-2,i=3,时间:Tue Aug 07 19:10:25 CST 2018
当前运行线程:线程-3,i=3,时间:Tue Aug 07 19:10:25 CST 2018
当前运行线程:线程-1,i=3,时间:Tue Aug 07 19:10:25 CST 2018
当前运行线程:线程-1,i=4,时间:Tue Aug 07 19:10:26 CST 2018
当前运行线程:线程-2,i=4,时间:Tue Aug 07 19:10:26 CST 2018
当前运行线程:线程-3,i=4,时间:Tue Aug 07 19:10:26 CST 2018
当前运行线程:线程-1,i=5,时间:Tue Aug 07 19:10:27 CST 2018
当前运行线程:线程-2,i=5,时间:Tue Aug 07 19:10:27 CST 2018
当前运行线程:线程-3,i=5,时间:Tue Aug 07 19:10:27 CST 2018
线程的加入:
假设在当前运行线程A中调用线程B的join()方法:
- 如果没有在join()方法中指定长度,则线程A会等到线程B运行结束后,才从阻塞状态转变为就绪状态,等待获取CPU
- 如果在join()方法中指定了长度,线程B没有运行结束,则线程A也会在时间结束时,从阻塞状态转变为就绪状态。
- 如果在join()方法中指定了长度,且时间长度超过线程B的运行时间,则线程A会等到线程B运行结束后,从阻塞状态转变为就绪状态
class ThreadDemo2 implements Runnable {
public void run() {
try {
Thread.sleep(1000); //线程休眠1s
}catch(InterruptedException e) {
e.printStackTrace();
}
for(int i = 1;i <= 5;i++) {
System.out.println("当前运行线程:"+Thread.currentThread().getName()
+",i="+i+",时间:"+(new Date()));
}
}
}
public class TestDemo8 {
public static void main(String[] args) {
ThreadDemo2 tm = new ThreadDemo2();
Thread t1 = new Thread(tm,"线程-1");
Thread main = Thread.currentThread();
t1.start();
for(int i = 0;i < 10;i++) {
if(i == 5) {
try {
t1.join(3000);
}catch(Exception e) {
e.printStackTrace();
}
}
System.out.println("main="+i+"-->"+new Date());
}
}
}
运行结果:
main=0-->Tue Aug 07 19:21:42 CST 2018
main=1-->Tue Aug 07 19:21:43 CST 2018
main=2-->Tue Aug 07 19:21:43 CST 2018
main=3-->Tue Aug 07 19:21:43 CST 2018
main=4-->Tue Aug 07 19:21:43 CST 2018
当前运行线程:线程-1,i=1,时间:Tue Aug 07 19:21:43 CST 2018
当前运行线程:线程-1,i=2,时间:Tue Aug 07 19:21:43 CST 2018
当前运行线程:线程-1,i=3,时间:Tue Aug 07 19:21:43 CST 2018
当前运行线程:线程-1,i=4,时间:Tue Aug 07 19:21:43 CST 2018
当前运行线程:线程-1,i=5,时间:Tue Aug 07 19:21:43 CST 2018
main=5-->Tue Aug 07 19:21:43 CST 2018
main=6-->Tue Aug 07 19:21:43 CST 2018
main=7-->Tue Aug 07 19:21:43 CST 2018
main=8-->Tue Aug 07 19:21:43 CST 2018
main=9-->Tue Aug 07 19:21:43 CST 2018
线程的唤醒:
- 线程的唤醒是指线程从休眠的阻塞状态转换为运行状态,可以通过Thread类的interrupt()方法
- isInterrupted():这是一个实例方法,用于判断当前线程是否已经被中断,是,返回true,否则返回false,线程中的中断状态不受该方法的影响。
- interrupted():这是一个静态方法,用于判断执行中的线程是否已经被中断,若是,返回true,否则发挥false,调用此方法后,线程的中断状态将被取消,即处于中断状态的线程将变为非中断状态
class ThreadDemo3 implements Runnable {
public void run() {
System.out.println("开始运行run()方法:"+new Date());
try {
Thread.sleep(5000);
}catch(Exception e) {
System.out.println("线程已经被唤醒!");
}
System.out.println("结束运行run()方法:"+new Date());
}
}
public class TestDemo9 {
public static void main(String[] args) {
ThreadDemo3 tm = new ThreadDemo3();
Thread t1 = new Thread(tm,"线程-1");
t1.start();
try {
t1.sleep(2000); //线程休眠
}catch(Exception e) {
e.printStackTrace();
}
t1.interrupt(); //线程唤醒
}
}
运行结果:
开始运行run()方法:Tue Aug 07 19:38:51 CST 2018
线程已经被唤醒!
结束运行run()方法:Tue Aug 07 19:38:53 CST 2018
后台线程:
如要将一个线程设置为后台线程,必须要在该线程启动前调用setDaemon()方法,设置后台线程后,还可以通过isDaemon()方法判断该程序是否已经成为后台线程
线程的礼让:
可以用yield()方法来实现,该方法只能把运行权让出来,不能让给指定线程,即yield()方法让出运行权后,哪个线程抢占到,哪个线程就运行
class ThreadDemo4 implements Runnable {
public void run() {
for(int i = 1;i <= 10;i++) {
System.out.println("当前运行的线程:"+Thread.currentThread().getName()
+",i="+i);
Thread.currentThread().yield();
}
}
}
class ThreadDemo5 implements Runnable {
public void run() {
for(int i = 1;i <= 10;i++) {
System.out.println("当前运行的线程:"+Thread.currentThread().getName()
+",i"+i);
}
}
}
public class TestDemo10 {
public static void main(String[] args) {
ThreadDemo4 tm1 = new ThreadDemo4();
ThreadDemo5 tm2 = new ThreadDemo5();
Thread t1 = new Thread(tm1,"线程-1");
Thread t2 = new Thread(tm2,"线程-2");
t1.start();
t2.start();
}
}
运行结果:
t1 线程运行的几率比t2线程运行的几率要小,因为t1线程总是让其他线程先执行
当前运行的线程:线程-2,i1
当前运行的线程:线程-2,i2
当前运行的线程:线程-1,i=1
当前运行的线程:线程-2,i3
当前运行的线程:线程-1,i=2
当前运行的线程:线程-2,i4
当前运行的线程:线程-1,i=3
当前运行的线程:线程-2,i5
当前运行的线程:线程-1,i=4
当前运行的线程:线程-2,i6
当前运行的线程:线程-2,i7
当前运行的线程:线程-1,i=5
当前运行的线程:线程-2,i8
当前运行的线程:线程-1,i=6
当前运行的线程:线程-2,i9
当前运行的线程:线程-1,i=7
当前运行的线程:线程-2,i10
当前运行的线程:线程-1,i=8
当前运行的线程:线程-1,i=9
当前运行的线程:线程-1,i=10
sleep()和yield()都可以让出运行权,但他们还是有很大的不同
- sleep()方法让出运行权时,不考虑其他线程的优先级;而yield()方法将运行权让给与其具有相同优先级或比其优先级更高的线程
- 调用sleep()方法会让当前线程转到阻塞状态;而调用yield()方法则将将当前线程转到就绪状态
- sleep()方法声明抛出InterruptedException异常;而yield方法抛出任何异常
- sleep()方法比yield()方法具有更好的移植性,不能依靠yield()方法来提高程序的并发性