Java线程状态转换

时间:2021-09-08 07:58:16

1.线程状态图

    当我们new 一个Thread的时候,就是告诉CPU我们创建一个新的线程,这个时候只是创建过程。当我们开始为创建的线程调用start()的方法时,我们的线程就进入的就绪状态。所以我们一般不直接开始调用run的方法。就绪状态好了之后,就会反选run的方法。如果出现某种特殊的情况,那么线程可以还会进入就绪状态。例如,某个线程的优先级比较高的时候,需要它先使用。当然,在线程执行的时候,可能出现者线程进入休眠状、wait()的时候,使用线程挂起,那么线程处于阻塞状态。只有线程运行结束后,再也得不到CPU时间,它就结束了。如图所示。

Java线程状态转换

2. 线程状态

我们继续上面的学习,这里我们可能说的线程状态词与上图不一样,我们用专业词来解说。

1)        新建(New):当线程被创建时,它会短暂地处于这种状态。此时它已经分配了必需的系统资源,并执行了初始化。此刻线程已经有资格获得CPU时间了,之后调度器将把这个线程转变为可运行状态或阻塞状态。

2)        就绪(Runnable):在这种状态下,只要调度器把时间片分配给线程,线程就可以运行。也就是说,在任意时刻可以运行也可以不运行。只需调度器能分配时间片给线程,它就可以运行;这不同于死亡和阻塞。

3)        阻塞(Blocked):线程能够运行,但有某个条件阻止它运行。线程处于阻塞状态时,调度器将忽略线程,不会分配给线程任何CPU时间。直到线程重新进入就绪状态,它才有可能执行操作。

4)        死亡(Dead):处于死亡或终止状态的线程将不再是可调度的。当线程再也不会得到CPU时间,它的任务已结束,或不再是可运行的。任务死亡的通常方式是从run()方法返回,但是任务的线程还可以被中断,你将要看到这一点。

3.进入阻塞状态

一个任务进入阻塞状态,可能的如下原因:

1)        通过调用sleep(milliseconds)使任务进入休眠状态,在这种情况下,任务在指定的时间内不会运行。

2)        你通过调用wait()使线程挡挂起。走到线程得到了notify()或notifyAll()消息,线程才会进入就绪状态。

3)        任务在等待某个输入、输出完成。

4)        任务试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个任务已经获取了这个锁。

4.线程控制基本方法

Java线程状态转换

5. sleep方法使用

下面的例子,我们使用了Thread.sleep()的方法,说明当前的主线程(main)将会休眠多久时间,所以主线程 在休眠期间thread.interrupt()是不会被执行的,但是另外的新线程依然会在运行。当主线程醒来之后,就会执行interruput()的方法。这个方法是在Thread类的包中,方法将设置线程的中断状态。如果一个线程已经阻塞,或者试图执行一个阻塞操作,那么设置这个线程的中断状态将抛出InterruptedException.

package com.owen.thread;

import java.util.Date;

/**
* 子线程在运行时,主线程休眠10秒
*
* @author OwenWilliam 2016-7-21
* @since
* @version v1.0.0
*
*/
public class TestInterrupt
{

public static void main(String[] args)
{
MyThread thread = new MyThread();
thread.start();

try
{
// 主线程休眠10秒
Thread.sleep(10000);
} catch (InterruptedException e)
{
}

// 断掉子线程,不要使用stop()方法,stop()会立刻停止,不会将flag设置为false
thread.interrupt();
}

}

/**
* 创建个线程,每隔一秒就打印出时间
*
* @author OwenWilliam 2016-7-21
* @since
* @version v1.0.0
*
*/
class MyThread extends Thread
{

public void run()
{
boolean flag = true;
while (flag)
{
System.out.println("====" + new Date() + "====");

try
{
// 休眠1秒
sleep(1000);
} catch (InterruptedException e)
{
flag = false;
return;
}
}
}
}

6. join方法使用

一个线程可以在其它线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行。如果某个线程在另一个线程t上调用t.join(),此线程被挂起,直到目标线程t结束才恢复(t.isAlive()返回为假)。届可以在调用join()时带上一个超时参数(单位可以是毫秒和纳秒),这样如果目标线程在这段时间到期还没有结束的话,join()方法总能返回。

    下面的例子就是实现将t1的线程合并到主线程上,原先是有两条线程,现在join()之后变成了只有一条主线程子。所以要等t1.start()的方法执行好了之后才可以执行后面的线程。

package com.owen.thread;

/**
* 使用join()的方法,将MyThread2的线程合并到主线程中, 这个时候就相当于调用方法了,
* 等待一个线程运行好了才可以运行下一个线程
*
* @author OwenWilliam 2016-7-21
* @since
* @version v1.0.0
*
*/
public class TestJoin
{

public static void main(String[] args)
{
MyThread2 t1 = new MyThread2("t1");
t1.start();
try
{
// 合并线程到主线程
t1.join();
} catch (InterruptedException e)
{
}
System.out.println("thread isAlive:" + t1.isAlive());
for (int i = 1; i <= 10; i++)
{
System.out.println("i am main thread");
}

}

}

/**
* 创建线程,每隔1秒打印一行信息
*
* @author OwenWilliam 2016-7-21
* @since
* @version v1.0.0
*
*/
class MyThread2 extends Thread
{
MyThread2(String s)
{
super(s);
}

public void run()
{
for (int i = 1; i <= 10; i++)
{
System.out.println("i am " + getName());

try
{
sleep(1000);
} catch (InterruptedException e)
{
return;
}
}
}
}

7.yield方法

    在下面的例子中,我们使用了静态方法Thread.yield()调用是对线程调度器(Java线程机制的一部分,可以将CPU从一个线程转移到另一个线程)的一种建议,它声明:”我已经执行完生命周期中最重要的部分了,此刻正是切换给其他任务执行一段时间的大好时机。”

package com.owen.thread;

/**
* 测试Thread线程的yield的方法
*
* @author OwenWilliam 2016-7-21
* @since
* @version v1.0.0
*
*/
public class TestYield
{

public static void main(String[] args)
{
MyThread3 t1 = new MyThread3("t1");
MyThread3 t2 = new MyThread3("t2");
t1.start();
t2.start();
}

}

/**
* 创建线程,当i的值为10的倍数时,那么就让出线程,给其它线程执行一会儿,注意是一会儿
*
* @author OwenWilliam 2016-7-21
* @since
* @version v1.0.0
*
*/
class MyThread3 extends Thread
{
MyThread3(String s)
{
super(s);
}

public void run()
{
for (int i = 1; i <= 100; i++)
{
System.out.println(getName() + ": " + i);
if (i % 10 == 0)
{
// 让出线程一会儿
yield();
}
}
}
}

8.线程的优先级别

    线程持优先级将线程的重要性传递给了调度器。尽管CPU处理现有线程集的顺序是不确定的,但是调度器将倾向于让优先权最高的线程先执行。然而,这并不意味着优先权较低的线程将得不到执行(也就是说,优先权不会导致死锁)。优先级较低的线程仅仅是执行频率较低。

1)        Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。

2)        线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5.

a)        Thread.MIN_PRIORITY = 1

b)        Thread.MAX_PRIOROTY = 10

c)        Thread.NORM_PRIORITY = 5

3)        使用下述方法获得或设置线程对象的优先级。

a)        int getPriority();

b)        void setPriority(intnewPriority);

    下面有例子中,我们分别在主线程上分出两条新的线程,它们分别 是t1和t2,所以正常情况下它们执行时间是相同的,也就是CPU会执行t1一会儿然后再执行t2一会儿,只是时间很快你感受不到,所以感觉就是多线程在执行。当我们在t1上加了优先级高之后,那么t1执行的时间就相对于t2要长一点了。

package com.owen.thread;

/**
* 测试线程的优先级使用
* @author OwenWilliam 2016-7-21
* @since
* @version v1.0.0
*
*/
public class TestPriority
{

public static void main(String[] args)
{
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
//t1线程的优先级加3
t1.setPriority(Thread.NORM_PRIORITY + 3);
t1.start();
t2.start();
}

}

/**
* 线程T1输出数据
* @author OwenWilliam 2016-7-21
* @since
* @version v1.0.0
*
*/
class T1 implements Runnable
{

@Override
public void run()
{
for (int i = 0; i < 1000; i++)
{
System.out.println("T1:" + i);
}

}

}

/**
* 线程T2的输出数据
* @author OwenWilliam 2016-7-21
* @since
* @version v1.0.0
*
*/
class T2 implements Runnable
{

@Override
public void run()
{
for (int i = 0; i < 1000; i++)
{
System.out.println("T2----" + i);
}

}

}