线程的基本操作
线程的基本操作
wait
调用wait
方法的线程会进入Waiting状态,只有等到其他线程的通知或被中断后才会返回。需要注意的是,在调用wait
方法后会释放对象的锁,因此wait方法一般被用于同步方法或同步代码块中。
sleep
sleep
的作用是让目前正在执行的线程休眠,让CPU去执行其他任务。从线程状态来说,就是从执行状态变为限时阻塞状态。sleep()
方法定义在Thread类中,是一组静态方法,有两个重载版本。
//使目前正在执行的线程休眠millis毫秒
public static native void sleep(long millis) throws InterruptedException;
//使目前正在执行的线程休眠millis毫秒,nanos纳秒
public static void sleep(long millis, int nanos) throws InterruptedException;
sleep()
方法会有InterruptedException
受检异常抛出,如果调用了sleep()方法,就必须进行异常审查,捕获InterruptedException
异常,或者再次通过方法声明存在InterruptedException
异常。
当线程睡眠时间满后,线程不一定会立即得到执行,因为此时CPU可能正在执行其他的任务,线程首先进入就绪状态,等待分配CPU时间片以便有机会执行。
interrupt
interrupt
方法用于向线程发送一个终止通知信号,会影响该线程内部的一个中断标识位,线程本身并不会因为调用了interrupt
方法而改变状态(阻塞、终止等)。状态的变化需要等待接收到的程序的最终处理结果来判定。
- 调用
interrupt
方法并不会中断一个正在运行的线程,也就是说处于运行状态的线程并不会因为调用了interrupt
方法而终止,仅仅改变了内部维护的中断标识位而已。 - 如果因为调用
sleep
方法使线程处于超时等待状态,则这时调用interrupt
方法会抛出InterrupedException
,使线程提前结束超时等待状态。 - 许多声明抛出
InterrupedException
的方法如Thread.sleep(long mills)
,在抛出异常前都会清除中断标识位,所以在抛出异常后调用isInterrupted
方法将返回false。 - 中断状态是线程固有的一个标识位,可以通过此标识位安全终止线程。比如,在想终止一个线程时,可以先调用该线程的
interrupt
方法,然后在线程的run
方法中根据该线程的isInterrupted
方法的返回状态值安全终止线程。
join
join
方法用于等待其他线程终止,如果在当前线程中调用一个线程的join
方法,则当前线程会转为阻塞状态,等到另一个线程结束,当前线程再由阻塞状态转为就绪状态,等待获取CPU使用权。在很多情况下,主线程生成并启动了子线程,需要等待子线程返回结果再退出,这时就要用到join
方法。
总结:A线程调用B线程的join()方法,等待B线程执行完成,在B线程没有完成前,A线程阻塞。
join()方法有三个重载版本:
void join()
:A线程等待B线程执行结束后,A线程重启执行。void join(long millis)
:A线程等待B线程执行一段时间,最长等待时间为millis毫秒。超过millis毫秒后,不论B是否结束,A线程重启执行。void join(long millis,int nanos)
:等待乙方线程执行一段时间,最长等待时间为millis毫秒加nanos纳秒。超过时间后,不论乙方是否结束,甲方线程都重启执行。
注意:
join()是实例方法不是静态方法,需要使用线程对象去调用,如
thread.join()
。调用join()时,不是thread所指向的目标线程阻塞,而是当前线程阻塞。
只有等到thread所指向的线程执行完成或超时,当前线程才能启动执行。
join
方法的作用是当调用该方法的线程在执行完run()
方法后,再执行join
方法后面的代码。简单来说,就是将两个线程合并,用于实现同步功能。具体而言,可以通过线程A的join()
方法来等待线程A的结束,或者使用线程A的join(2000)
方法来等待线程A的结束,但最多只等待2s。
public class JoinTest1 {
static class JoinThread implements Runnable{
@Override
public void run() {
System.out.println("begin...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end...");
}
}
public static void main(String[] args) {
Thread t = new Thread(new JoinThread());
t.start();
try {
t.join(1000);//主线程等待t结束,只等1s
if(t.isAlive()){
System.out.println("t has not finished");
}else{
System.out.println("t has finished");
}
System.out.println("joinFinish");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这段代码主线程最多等待1秒之后,开始执行t.join(1000)
后面的代码
将t.join(1000)
换成t.join()
之后,输出结果如下:
实际项目中很少单独创建线程,而是使用Executor,所以join()的使用场景并不多。
yield
调用yield
方法会使当前线程让出(释放)CPU时间片,与其他线程一起重新竞争CPU时间片。在一般情况下,优先级高的线程更有可能竞争到CPU时间片,但这不是绝对的。
notify
Object
类有个notify
方法,用于唤醒在此对象监视器上等待的一个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,唤醒时选择是任意的。
我们通常调用其中一个对象的wait方法在对象的监视器上等待,直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程,被唤醒的线程将以常规方式与在该对象上主动同步的其他线程竞争。
类似的方法还有notifyAll
,用于唤醒在监视器上等待的所有线程。
public class WaitNotifyExample {
public static void main(String[] args) throws InterruptedException {
final Object lock=new Object();//作为锁对象
//线程A
Thread threadA = new Thread(() -> {
synchronized (lock) {
System.out.println("Thread A is waiting...");
try {
lock.wait();//线程A等待并释放锁
System.out.println("Thread A is resumed.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(() -> {
synchronized (lock) {
System.out.println("Thread B is working");
lock.notify();//唤醒一个等待的线程A
// lock.notifyAll();//唤醒所有等待的线程
}
});
threadA.start();
Thread.sleep(1000);
threadB.start();
}
}
上述代码中,我们创建了两个线程A和B,它们共享一个对象锁
lock
。线程A首先进入同步块,调用lock.wait()
方法进入等待状态,并释放了lock
对象的锁。接下来线程B进入同步块,执行一些操作后,通过lock.notify()
方法唤醒了一个正在等待的线程A。线程A被唤醒后继续执行,并在控制台输出相关信息。这里在主线程中使用
Thread.sleep(1000)
暂停了一段时间,以确保线程A先进入等待状态。
daemon
setDaemon去看守护线程
sleep和wait方法的区别
共同点:两者都可以暂停线程的执行。
区别:
sleep()
方法没有释放锁,而wait()
方法释放了锁。wait
通常用于线程间交互/通信,sleep
通常被用于暂停执行。sleep
方法暂停执行指定的时间,让出CPU给其他线程,但其监控状态依然保持,在指定的时间过后又会自动恢复到运行状态。调用
wait()
方法时,线程会放弃对象锁,进入等待锁池,只有针对此对象调用notify()
或者notifyAll()
方法后,该线程才能进入对象锁池准备获取对象锁,并进入运行状态。sleep
方法属于Thread类,wait
方法属于Object类。
- 点赞
- 收藏
- 关注作者
评论(0)