线程的基本操作

举报
别团等shy哥发育 发表于 2023/10/18 13:36:51 2023/10/18
【摘要】 线程的基本操作 wait调用wait方法的线程会进入Waiting状态,只有等到其他线程的通知或被中断后才会返回。需要注意的是,在调用wait方法后会释放对象的锁,因此wait方法一般被用于同步方法或同步代码块中。 sleepsleep的作用是让目前正在执行的线程休眠,让CPU去执行其他任务。从线程状态来说,就是从执行状态变为限时阻塞状态。sleep()方法定义在Thread类中,是一组静...

线程的基本操作

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();
        }
    }
}

image-20230824222717086

这段代码主线程最多等待1秒之后,开始执行t.join(1000)后面的代码

t.join(1000)换成t.join()之后,输出结果如下:

image-20230824223113115

实际项目中很少单独创建线程,而是使用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();
    }
}

image-20230828104122093

上述代码中,我们创建了两个线程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类。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。