-
线程常用方法
-
Join方法
•join方法:执行该方法的线程进入阻塞状态,直到调用该方法的线程结束后再由阻塞转为就绪状态。
要注意的一点就是:执行方法的线程与调用方法的线程并不是一个概念,例如在图片中我们看到,计数器线程本应该循环输出的,但是在i=2时执行了join方法,而join方法由时间线程调用,此时计数器线程便进入了阻塞状态,转而时间线程进入了运行状态。开始循环输出,直到其执行完毕,计数器线程再次转为就绪状态,由于此时没有其他线程,就顺利转为运行状态,继续输出直到结束。
值得注意的是:线程对象在调用join方法前必须先调用start方法,否则该线程永远不会进入执行状态。
在这段代码中,由于时间线程并没有先调用start方法,没有开始启动,因此无法调用join方法,原因在后面会提及
-
interrupt方法
•interrupt方法:结束线程在调用Object类的wait方法或该类的join方法、sleep方法过程中的阻塞状态,并在调用wait、join和sleep方法处产生InterruptedException异常。
在这段代码中,由于我们在时间线程的run方法中植入了休眠代码,本来是应该直到30秒后才输出afterTime,但主线程中的时间线程对象调用了interrupt方法,使得休眠导致的阻塞状态终止,于是afterTime便被输出,同时由于sleep被try-catch包裹,其终止导致了异常被捕获。
-
currentThread方法
currentThread方法:返回当前正在执行的线程对象
我们来区别一下几段代码
public class Test {
public static void main(String[] args) {
Thread thread = new TimeThread();
System.out.println(thread);
thread.start();
}
}
class TimeThread extends Thread{
@Override
public void run() {
Thread thread = Thread.currentThread();//返回的是时间线程
System.out.println(thread);
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
public static void main(String[] args) {
new TimeThread().start();
}
}
class TimeThread extends Thread{
public TimeThread(){
super("时间线程");
}
@Override
public void run() {
printTime();
}
public void printTime(){
Thread thread = Thread.currentThread();//返回时间线程
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println(thread.getName()+",当前时间:"+time);
}
}
public class Test {
public static void main(String[] args) {
TimeThread timeThread = new TimeThread();
System.out.println("########"+timeThread);
timeThread.start();//返回时间线程
timeThread.run();//返回主线程
}
}
class TimeThread extends Thread{
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println("@@@@@@@@"+thread);
}
}
为什么结果是这样呢?这里我们又要弄明白“执行”某个方法的概念了,像第一段代码中的thread.start();,代表着主程序执行了start方法,而start方法是由时间线程对象调用的,不是它在执行,而这个对象执行的,是其所在类的run方法,由于run方法中有currentThread方法,因此这个对象就执行了这个方法,所以方法返回的就是时间线程对象;区别的一点就是最后一段代码的timeThread.run(),这里我们看到,run方法是在主程序里就执行了,因此返回的对象为主线程。
-
isAlive方法
•isAlive方法:判定该线程是否处于就绪、运行或阻塞状态,如果是则返回true,否则返回false。
我们要注意。isAlive方法判断的是线程是否处于就绪,运行,阻塞三种状态中的任意一种,通过线程的生命周期我们可以换言之,该方法实际上只要线程start了,且未dead,就会返回true,在上述代码中,主线程的输出语句一定是true,因为如果主线程生命周期未结束,就处于三种状态之中,如果已经结束,那么也没有输出语句了;而打印线程在start后,其run方法既有可能输出false,也有可能输出true,因为主线程和打印线程要抢占cpu的控制权,如果主线程成功获取的话,它就结束了,因此run方法返回false,如果打印线程先抢夺到控制权,此时主线程还未结束,则返回true。
前面我们提到join方法不会被一个尚未start的线程调用,这是因为join方法中就使用了isAlive方法来判断调用它的线程是否存活,未启动或已死亡的线程都是无法调用成功的
-
setDaemon方法
setDaemon方法:用于将一个尚未调用线程start方法的线程设置为守护线程。守护线程主要用于为其他线程的运行提供服务(Java中的垃圾回收机制就是守护线程),这种线程属于创建它的线程
•注意:
1.守护线程随着最后一个非守护线程的终止而终止,如下代码:
在这个方法中,计数线程被设为了守护线程,并且启动,随后开始循环输出计数,随着主线程sleep结束,主线程也结束了,该程序中便不再有非守护线程存活,因此计数线程终止。
同时我们也需要注意:守护线程的终止条件是——最后一个非守护线程终止 ,因此,只要程序中尚有非守护线程,所有的守护线程都会继续存活(除非自己运行结束)。除了调用其他特殊方法,守护线程互不影响存活
,并且某个特定的非守护线程也对守护线程的存活没有影响;对守护线程存活有影响的,有且只有“最后一个存活者的非守护线程”,这个线程就是“压死守护线程的最后一棵稻草”。
-
其他方法
•void start():使该线程开始启动,Java 虚拟机负责调用该线程的 run() 方法。多次启动一个线程是非法的。
•void sleep(long millis):Thread类静态方法,线程进入阻塞状态,在指定时间(单位为毫秒)到达之后进入就绪状态(Runnable),而非立即进入执行状态。
•void yield():静态方法,当前线程放弃占用CPU资源,回到就绪状态,使其他优先级不低于此线程的线程有机会被执行。
这个方法值得注意的是,它是使运行状态的线程回归到就绪状态,因此原来就绪区的线程“有机会”被执行,并不代表它们一定会执行,因为这个方法也到了就绪区,可以和其他线程继续抢占cpu,如果抢占成功的话,执行的还是这个线程。
•void setPriority(int newPriority):设置当前线程的优先级,线程优先级越高,线程获得执行的次数越多,Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
1.static int MAX_PRIORITY 最高优先级值为10;
2.static int NORM_PRIORITY 默认优先级值为5;
3.static int MIN_PRIORITY 最低优先级值为1;
注意:同一个线程类创建的多个线程,线程优先级越高的线程获得的执行次数极有可能越多;但是不同线程类创建的线程,线程优先级越高,执行次数不一定越多,这个run方法的代码复杂度有关。
这就好比有一条公路(程序)上有许多货车(线程)在开,通过给它们安装更好的发动机(分配更高的优先级),它们会跑得比原来快,但是不是比对方快,还要看它们的运载量大不大(run方法的复杂程度),重量太重的话,发动机再强也可能跑不过其他轻量车。
int getPriority():获得当前线程的优先级。