线程中的Thread类

Thread类

1.1 步骤:
  • 定义一个Thread类的子类,重写run()方法,将相关逻辑实现,run()方法就是线程要执行的业务逻辑
  • 创建自定义的线程类子对象
  • 调用子类实例的start()方法来启动线程
1.2 Thread类详解

Thread类也是实现了Runnable接口:

public class Thread implements Runnable{}

在这里插入图片描述
Thread类重载了很多构造器:
(1)无参构造函数:如果没有显示的指定线程的名称,那么线程会以“Thread-”作为前缀与一个自增数字进行组合,自增数字在整个JVM进程中将不断自增
在这里插入图片描述
(2)将Runnable实现类作为参数传入
在这里插入图片描述

Thread中常用的方法:

(1)start()方法:start()用来启动一个线程,当调用start方法后线程并没有立即执行而是处于就绪状态,这个就绪状态是指该线程已经获取了除CPU资源外的其他资源,当获得CPU资源后线程才会执行

在这里插入图片描述

(2)run()方法:Thread类中的run()方法是实现了Runnable接口中的run()方法,run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得CPU执行时间,便进入run方法体去执行具体的任务

在这里插入图片描述

(3)sleep()方法:sleep相当于让线程睡眠,让出CPU,让CPU去执行其他任务,也就是在这期间不参与CPU的调度,但是该线程所拥有的监视器资源,比如锁还是持有不让出的,指定的睡眠时间到了后该函数会正常返回,线程就处于就绪状态,如果在睡眠期间其他线程调用了该线程的interrupt()方法中断了该线程,则该线程会在调用sleep方法的地方抛出InterruptException异常而返回

在这里插入图片描述

sleep有两个版本:
在这里插入图片描述
在这里插入图片描述

(4)yield()方法:Thread类中有一个静态的yield方法,当一个线程调用yield方法时,实际上就是在暗示线程调度器当前线程请求让出自己的CPU使用,但是线程调度器可以无条件忽略这个暗示。

在这里插入图片描述

补充:sleep()方法和yield()方法的区别在于,当线程调用sleep()方法时调用线程会被阻塞挂起指定的时间,在这期间线程调度器不会去调度该线程,而调用yield方法时,线程只是让出自己剩余的时间片,并没有被阻塞挂起,而是处于就绪态,线程调度器下一次调度时就有可能调度到当前线程执行
(5)join()方法:

我们先看一下join方法的源码:
在这里插入图片描述
在join方法中,我们发现,它调用了wait()方法

public class joinTest {
    public static void main(String[] args) throws InterruptedException{
        //创建线程一
        Thread threadOne=new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("child threadOne over!");
            }
        });
        Thread threadTwo=new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("child threadTwo over!");
            }
        });

        //启动线程
        threadOne.start();
        threadTwo.start();
        System.out.println("wait all child thread over!");

        //等待子线程执行完毕,返回
        threadOne.join();
        threadTwo.join();
        System.out.println("all child thread over!");
    }
}

执行结果:
在这里插入图片描述
当主线程调用ThreadOne的join()方法时,主线程被阻塞,当执行完线程一时,主线程又调用ThreadTwo的join()方法,主线程再次被阻塞,当等待完线程ThreadTwo执行完毕时,主线程才会继续开始执行

(6)wait()方法:当一个线程调用一个共享变量的wait()方法时,该调用线程会被阻塞挂起

wait()方法具有三个方式的重载:
1)无参wait()方法:
在这里插入图片描述
2)wait(long timeout)函数
在这里插入图片描述
该方法比无参wait()方法多了一个超时参数,它的不同之处在于:如果一个线程调用共享变量的wait()方法挂起后,没有在指定的timeout ms时间内被其他线程调用该共享变量的notify()或者notifyAll()唤醒后,那么该函数还是会因为超时而返回
3)wait(long timeout,int nanos)函数
在这里插入图片描述
当一个线程调用了一个共享对象的wait()方法时,该调用线程会被阻塞挂起,直到发生 下面几件事之一才会返回:

  • 其他线程调用了该共享对象的notify()或者notifyAll()方法
  • 其他线程调用了该线程的interrupt()方法,该线程抛出InterruptedException异常返回

那么一个线程如何才能获得一个共享变量的监视器锁呢
执行synchronied同步代码块时,使用该共享变量作为参数:

synchronized(共享变量){
	//dosomething
}

调用该共享变量的方法,并且该方法使用了synchronized修饰

synchronized void add(int a,int b){
	//dosomething
}
(7)notify()函数:一个线程在获取到了某共享变量的监视器锁后,可以调用共享变量的notify()方法,接着会唤醒一个在该共享变量上调用wait系列方法后被挂起的函数,一个共享变量可能会有多个线程在等待,具体唤醒哪个等待的线程是随机的

被唤醒的线程的线程不能马上从wait()方法返回并继续执行,它必须在获取了共享对象的监视器锁后才能返回,也就是唤醒它的线程释放了共享变量上的监视器锁后,被唤醒的线程也不一定会获取到共享对象的监视器锁,这是因为该线程还需要和其他线程一起竞争该锁,只有该线程竞争到了共享变量的监视器锁后才可以继续执行

(8)notifyAll()函数:不同于在共享变量上调用notify()函数会唤醒被阻塞到该共享变量上的一个线程,notifyAll()方法则会唤醒所有在该共享变量上由于调用wait()方法而被阻塞挂起的线程

我们来看一个例子,看notify()和notifyAll()的区别

public class notifyAllTest {
    //创建资源
    private static volatile Object resourceA=new Object();

    public static void main(String[] args) throws InterruptedException {
        //创建线程
        Thread threadA=new Thread(new Runnable(){
           public void run(){
               //获取resourceA共享资源的监视器锁
               synchronized (resourceA){
                   System.out.println("threadA get resourceA lock");
                   try{
                       System.out.println("threadA begin wait");
                       //线程A调用了共享对象resourceA的wait()方法,被阻塞
                       resourceA.wait();
                       System.out.println("threadA end wait");
                   }catch (InterruptedException e){
                       e.printStackTrace();
                   }
               }

           }
        });
        //创建线程
        Thread threadB=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA){
                    System.out.println("threadB get resourceA lock");
                    try{
                        System.out.println("threadB begin wait");
                        resourceA.wait();
                        System.out.println("threadB end wait");
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        });

        //创建线程
        Thread threadC=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA) {
                    System.out.println("threadC begin notify");
                    //线程C调用共享资源的notifyAll()方法
                    resourceA.notify();
                }
            }
        });

        //启动线程
        threadA.start();
        threadB.start();
        Thread.sleep(1000);
        threadC.start();

        //等待线程结束
        threadA.join();
        threadB.join();
        threadC.join();
        System.out.println("main over");
    }
}

当调用notify()函数时:
在这里插入图片描述
当调用notifyAll()时:
在这里插入图片描述

(9)interrupt()方法:

在这里插入图片描述
interrupt()方法仅是将线程的中断标志设置为true,并不是真正意义上的中断该线程的运行,所以如果没有相应的处理,该线程还是会继续执行下去,比如在while循环中使用**interrupted()或者isInterrupted()**来检查线程的中断标志位,从而做出相应的中断处理
当线程被阻塞的时候,比如线程被Object.wait,Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,==因为没有占用CPU运行的线程是不可能给自己的中断状态置位的,这样就会产生一个InterruptedException异常(该线程必须事先预备好处理此异常,可以使用catch捕获这个异常从而进行相应的处理),从而提早地终结被阻塞状态,==如果线程没有被阻塞,这是调用interrupt()方法将不起作用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值