Thread类的使用

文章目录

          • 1 线程的状态
          • 2 上下文切换
          • 3 Thread类中的常用方法
            • 3.1 start() 方法
            • 3.2 run() 方法
            • 3.3 sleep() 方法
            • 3.4 yield() 方法
            • 3.5 join() 方法
            • 3.6 interrupt() 方法
            • 3.7 stop()方法 destroy()方法
            • 3.8 有关线程属性的几个方法
          • 4 Thread类中方法的调用对线程状态变化的影响

1 线程的状态

线程的状态包括:
创建(new) 就绪(runnable) 运行(running) 阻塞(blocked , time waiting, waiting) 消亡


Thread类的使用_第1张图片

2 上下文切换

简单来说,对于线程的上下文切换实际上就是 存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行

3 Thread类中的常用方法

priority表示线程的优先级(最大值为10,最小值为1,默认值为5),daemon表示线程是否是守护线程,target表示要执行的任务

3.1 start() 方法

start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

3.2 run() 方法

run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务

3.3 sleep() 方法

sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。
sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象
如果调用了sleep方法,必须捕获InterruptedException异常或者将该异常向上层抛出。当线程睡眠时间满后,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务。所以说调用sleep方法相当于 让线程进入阻塞状态
sleep() 方法 有两个重载版本:

sleep(long millis)  //参数为毫秒
sleep(long millis,int nanoseconds)  //第一参数为毫秒,第二个参数为纳秒
public class SleepTest {

    private int i = 10;
    private Object object = new Object();

    public static void main(String[] args) throws IOException {
        SleepTest test = new SleepTest();
        MyThread thread1 = test.new MyThread();
        MyThread thread2 = test.new MyThread();
        thread1.start();
        thread2.start();
    }

    class MyThread extends Thread{

        public void run(){

            synchronized (object){
                i++;
                System.out.println("i:"+i);
                try{
                    System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠时间");
                    Thread.currentThread().sleep(10000);
                }catch (InterruptedException e){

                }
                System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束");
                i++;
                System.out.println("i:"+i);
            }
        }
    }
}

输出结果

i:11
线程 Thread-0 进入睡眠状态
线程 Thread-0 睡眠结束
i:12
i:13
线程 Thread-1 进入睡眠状态
线程 Thread-1 睡眠结束
i:14

sleep()方法不会释放锁
从上面输出结果可以看出,当Thread-0进入睡眠状态之后,Thread-1并没有去执行具体的任务。只有当Thread-0执行完之后,此时Thread-0释放了对象锁,Thread-1才开始执行。

3.4 yield() 方法

调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样 不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法 只能让拥有相同优先级的线程有获取CPU执行时间的机会
注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

3.5 join() 方法

假如在main线程中,调用 thread.join() 方法,则main方法会 等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的时间。
join方法有三个重载版本

join()
join(long millis)     //参数为毫秒
join(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒
public class JoinTest {

    public static void main(String[] args) {

        System.out.println("进入线程"+Thread.currentThread().getName());
        JoinTest test = new JoinTest();
        MyThread thread1 = test.new MyThread();
        thread1.start();
        printNums();
        try{
            System.out.println("线程"+Thread.currentThread().getName()+"等待");
            thread1.join();
            System.out.println("线程"+Thread.currentThread().getName()+"继续执行");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        printNums();
    }

    private static void printNums() {
        System.out.println("123456");
    }

    class MyThread extends Thread{
        @Override
        public void run(){
            System.out.println("进入线程"+Thread.currentThread().getName());
            try{
                Thread.currentThread().sleep(5000);
            }catch (InterruptedException e){

            }
            System.out.println("线程"+Thread.currentThread().getName()+"执行完毕");

        }
    }
}

输出结果

进入线程main
123456
线程main等待
进入线程Thread-0
线程Thread-0执行完毕
线程main继续执行
123456

可以看出,当调用thread1.join()方法后,main线程会进入等待,然后等待thread1执行完之后再继续执行
实际上调用 join()方法是调用了Object的 wait()方法,wait()方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限
由于wait()方法会让线程释放对象锁,所以 join()方法同样会让线程释放对一个对象持有的锁

3.6 interrupt() 方法

interrupt,顾名思义,即中断的意思。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt() 方法 和isInterrupted() 方法 来停止正在运行的线程

public class Test {
   public static void main(String[] args) throws IOException  {
      Test test = new Test();
      MyThread thread = test.new MyThread();
      thread.start();
      try {
         Thread.currentThread().sleep(2000);
      }catch (InterruptedException e) {
      }
      thread.interrupt();
   }
   
    class MyThread extends Thread{
       public void run() {
          try {
            System.out.println("进入睡眠状态");
            Thread.currentThread().sleep(10000);
            System.out.println("睡眠完毕");
          }catch (InterruptedException e) {
            System.out.println("得到中断异常");
          }
          System.out.println("run方法执行完毕");
       }
    }
}

输出结果:

进入睡眠状态
得到中断异常
run方法执行完毕

从这里可以看出,通过 interrupt()方法 可以中断处于阻塞状态的线程。那么能不能中断处于非阻塞状态的线程呢?

public class Test {

    public static void main(String[] args) throws IOException  {
        Test test = new Test();
        MyThread thread = test.new MyThread();
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread{
        @Override
        public void run() {
            int i = 0;
            while(i<Integer.MAX_VALUE){
                System.out.println(i+" while循环");
                i++;
            }
        }
    }
}

运行该程序会发现,while循环会一直运行直到变量i的值超出Integer.MAX_VALUE。所以说 直接调用 interrupt()方法 不能中断正在运行中的线程
但是如果配合 isInterrupted()能够中断正在运行的线程,因为调用 interrupt()方法相当于将中断标志位 置为true,那么可以通过调用 isInterrupted()判断中断标志是否被置位来中断线程的执行

public class Test {

    public static void main(String[] args) throws IOException  {
        Test test = new Test();
        MyThread thread = test.new MyThread();
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread{
        @Override
        public void run() {
            int i = 0;
            while(!isInterrupted() && i<Integer.MAX_VALUE){
                System.out.println(i+" while循环");
                i++;
            }
        }
    }
}

运行会发现,打印若干个值之后,while循环就停止打印了。
但是一般情况下不建议通过这种方式来中断线程,一般会在MyThread类中增加一个属性 isStop来标志是否结束while循环,然后再在while循环中判断isStop的值。

class MyThread extends Thread{
    private volatile boolean isStop = false;
    @Override
    public void run() {
        int i = 0;
        while(!isStop){
            i++;
        }
    }

    public void setStop(boolean stop){
        this.isStop = stop;
    }
}

那么就可以在外面通过调用setStop方法来终止while循环

3.7 stop()方法 destroy()方法

均已废弃

3.8 有关线程属性的几个方法
  • getId:用来得到线程ID
  • getName和setName:用来得到或者设置线程名称
  • getPriority和setPriority:用来获取和设置线程优先级
  • setDaemon和isDaemon:用来设置线程是否成为守护线程和判断线程是否是守护线程
  • 静态方法currentThread():获取当前线程

守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

4 Thread类中方法的调用对线程状态变化的影响

Thread类的使用_第2张图片

你可能感兴趣的:(Java并发编程)