多线程2

  • 2.0 Thread类是什么?
  • 2.1 Thread的常见构造方法
  • 2.2 Thread的常见属性
  • 2.3 start()_启动一个线程
  • 2.4 线程中断
  • 2.4.1 通过共享标记
  • 2.4.2 调用interrupt()方法
  • 2.4.3 interrupt()、interrupted()、isInterrupted()
  • 小结
  • 2.5 join()_等待一个线程
  • 2.6 获取当前线程引用
  • 2.7 线程休眠
  • 2.8 其他方法
  • 2.9 补充




1.多线程

2.0 Thread类是什么?

答:Thread 类是线程的描述类,每个线程都有一个 Thread 对象与之关联。

详述:

  • Thread 类是 JVM 用来管理线程的一个类。
  • 每个线程都有一个唯一的 Thread 对象与之关联。

2.1 Thread的常见构造方法

方法

说明

Thread()

创建线程对对象

Thread(Runnable target)

使用 Runnable 对象创建线程对象

(target 为描述任务的对象)

Thread(String name)

创建线程对象并命名

Thread(Runnable target , String name)

使用 Runnable 对象创建线程对象并命名

public class ThreadName {
    private static class MyThread extends Thread {

        public MyThread(String name) {
            super(name);
        }

        public MyThread() {

        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        // new Thread 创建线程
        // (1)java内部线程对象的创建
        // (2)系统创建线程,比较耗费时间和性能
        Thread t1 = new MyThread();
        Thread t2 = new MyThread("我是AAA");
        Thread t3 = new Thread(new MyThread());
        Thread t4 = new Thread(new MyThread(), "我是BBB");
        
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

用jconsole观察:

java thread 串行 java中thread类_ide

2.2 Thread的常见属性

属性

获取方法

补充

ID

getId()

线程的唯一标识,不同线程ID不会重复

名称

getName()

在调试工具中会用到

状态

getState()

表示线程当前状态

优先级

getPriority()

优先级高的线程理论上更容易被调度

是否后台线程

isDaemon()

JVM会在一个进程的所有非后台进程结束后,才结束运行

是否存活

isAlive()

run方法是否运行结束

(调用考虑点:非运行态不能执行到这行代码,如阻塞态、就绪态)

是否中断

isInterrupted()

线程是否中断

public class ThreadFields {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            Thread current = Thread.currentThread();
            System.out.println(current.getId());
            System.out.println(current.getName());
            System.out.println(current.getPriority());
            System.out.println(current.getState());
            System.out.println(current.isAlive());
            /*
            不能用this 此时this指的是Runnable对象 不是Thread对象
            System.out.println(this.isInterrupted());
            System.out.println(this.isDaemon());
             */
        }
    }
    private static class MyThread extends Thread {
        @Override
        public void run() {
            Thread current = Thread.currentThread();
            System.out.println(current.getId());
            System.out.println(current.getName());
            System.out.println(current.getPriority());
            System.out.println(current.getState());
            System.out.println(current.isAlive());
            //可以用this
            System.out.println(this.isInterrupted());
            System.out.println(this.isDaemon());
        }
    }

    public static void main(String[] args) {
       new MyThread().start();
    }
}

运行结果:

java thread 串行 java中thread类_System_02

2.3 start()_启动一个线程

注意:

  • 线程对象被创建出来并不意味着线程就开始运行。
  • 启动线程必须调用start()。
  • run方法 和 start方法不同。
  • run() 为线程运行态时所执行任务的方法,如果使用Runnable传入构造创建线程,就执行Runnable对象的run方法。

假设:只调用run(),不调用start(),结果会是什么?

public class StartOrRun {
    private static class MyThread extends Thread {
        @Override
        public void run() {
            while (true) {
                System.out.println("MyThread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t = new MyThread();
        t.run();
        /* 只执行 MyThread 不执行 main
           不要调用 run
        */
        while (true) {
            System.out.println("main");
            Thread.sleep(1000);
        }
    }
}

运行结果:只输出 MyThread 不输出mian,根本达不到多线程应有的效果。所以,线程启动一定要调用start()

java thread 串行 java中thread类_java thread 串行_03

2.4 线程中断

2.4.1 通过共享标记

注意:这种方法有延迟

import javafx.scene.layout.Priority;
import javax.swing.plaf.nimbus.State;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

public class StopThread1 {
    private static class Worker extends Thread {
        private volatile boolean quit = false; 
        //自定义共享标记

        Worker() {
            super("李四");
        }

        public void setQuit(boolean quit) {
            this.quit = quit;
        }

        @Override
        public void run() {
            while (!quit) {
                System.out.println(this.getName() + ": 我正在转账,别烦我");
                try {
                    //结果1
                    Thread.sleep(3000);
                    //结果2  //Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println(this.getName() + ": 对方是骗子,我不转账了");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Worker worker = new Worker();
        worker.start();
        System.out.println("我是张三,正在等李四转账");
        Thread.sleep(10 * 1000);
        System.out.println("打听到对方是骗子,通知李四停止转账");
        worker.setQuit(true);
        System.out.println("通知李四完毕");
        worker.join();
        System.out.println("李四停止转账");
    }
}

运行结果1:因为10不能整除3,所以有延迟(李四不能立刻做出反应)。

java thread 串行 java中thread类_ide_04

运行结果2:因为10/5可以整除,故李四立马做出反应。

java thread 串行 java中thread类_创建线程_05

2.4.2 调用interrupt()方法

  • 通过 thread 对象调用 interrupt() 方法通知该线程停止运行。
  • thread 收到通知的方式有两种:
  1. 如果线程调用了 wait/join/sleep 等方法而阻塞挂起,则以InterruptedException 异常的形式通知,清除中断标志
  2. 否则,只是内部的一个中断标志被设置,thread 可以通过
    2.1 Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
    2.2Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志

      第二种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到

public class StopThread2 {
    private static class Worker extends Thread {
        Worker() {
            super("李四");
        }

        @Override
        public void run() {
            while (!this.isInterrupted()) {
                System.out.println(this.getName() + ": 我正在转账,别烦我");
                try {
                    Thread.sleep(50 * 1000);
                } catch (InterruptedException e) {
                    System.out.println("我从睡梦中惊醒");
                    break;
                }
            }
            System.out.println(this.getName() + ": 对方是骗子,我不转账了");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Worker worker = new Worker();
        worker.start();
        System.out.println("我是张三,正在等李四转账");
        Thread.sleep(10 * 1000);
        System.out.println("打听到对方是骗子,通知李四停止转账");
        worker.interrupt(); // 中断
        System.out.println("通知李四完毕");
        worker.join();
        System.out.println("李四停止转账");
    }
}

运行结果:张三只会等待10s,立即通知李四(调用 interrupt 方法),李四立刻做出响应(无延迟)。

java thread 串行 java中thread类_创建线程_06

2.4.3 interrupt()、interrupted()、isInterrupted()

方法

说明

public void interrupt()

中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位

public static

判断当前线程的中断标志位是否设置,调用后清除标志位

public boolean isInterrupted()

判断对象关联的线程的标志位是否设置,调用后不清除标志位

  • interrupt()
  • java thread 串行 java中thread类_java thread 串行_07

  • 注意:如果是从catch (InterruptedException e) { System.out.println(this.isInterrupted()); System.out.println("我从睡梦中惊醒"); break; }分支跳出的,this.isInterrupted()依然打印的是false。
  • interrupted()
  • java thread 串行 java中thread类_System_08

  • isInterrupted()
  • java thread 串行 java中thread类_创建线程_09

测试:

public class StopThread3 {
    private static class Worker extends Thread {
        Worker() {
            super("李四");
        }

        @Override
        public void run() {
            while (true) {
                //第一次测试  
                //返回当前中断标志位,并重置标志位
                System.out.println(Thread.interrupted()); //一次true 后全是false
                //第二次测试
                //返回当前中断标志位 不做任何修改
                //System.out.println(this.isInterrupted()); //一直是true
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Worker worker = new Worker();
        worker.start(); // t.isInterrupted=false
        worker.interrupt(); //中断 t.isInterrupted=true
    }
}

第一次运行结果:调用 Thread.interrupted() ,第一次输出 true ,后一直输出 false 。

java thread 串行 java中thread类_ide_10

第二次运行结果:调用 this.isInterrupted() ,一直输出 true 。

java thread 串行 java中thread类_ide_11

小结

  1. InterruptedException 被抛出,则interrupt status 被重置(=false)
  2. Thread.interrupted() 首次返回真正的线程状态,然后 interrupt status = false
  3. this.isInterrupted() 不会重置

测试:

public class InterruptThread {
    private static class MyThread1 extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(10_000_000);
                } catch (InterruptedException e) {
                    // 通过异常的方式通知中断 我收到了中断通知 
                    System.out.println(isInterrupted()); //false
                    break;
                }
            }
            System.out.println("退出");
        }
    }

    private static class MyThread2 extends Thread {
        @Override
        public void run() {
            while (!isInterrupted()) {
            }
            // 通过判断中断状态退出,状态不会被重置
            System.out.println(isInterrupted()); //true
            System.out.println("退出");
        }
    }

    private static class MyThread3 extends Thread {
        @Override
        public void run() {
            while (!Thread.interrupted()) {
            }
            // 通过判断中断状态退出,状态被重置
            System.out.println(isInterrupted()); //false
            System.out.println("退出");
        }
    }

    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.start();
        t1.interrupt();
        MyThread2 t2 = new MyThread2();
        t2.start();
        t2.interrupt();
        MyThread3 t3 = new MyThread3();
        t3.start();
        t3.interrupt();
    }
}

运行结果:

java thread 串行 java中thread类_System_12

2.5 join()_等待一个线程

作用:当前线程(代码行所在线程)阻塞并等待,直到满足条件后,当前线程继续往下执行。

条件:无参时线程的引用对象执行完毕;有参时为引用线程执行完毕或时间耗完。

方法

说明

public void join()

等待线程结束

public void join(long millis)

当前线程阻塞并等待引用线程结束,最多等待 millis 毫秒

public void join(long millis, int nanos)

同理,但精度更高

public class MultiThreadFinishThenDoMain {
    public static void main(String[] args) throws InterruptedException {
        Thread threads[] = new Thread[10];
        for(int i = 0; i < 10;i++){
            final int j =i;
            Thread t =new Thread(()->{
                System.out.println(j);
            });
            t.start();
            threads[i] = t;
        }
        for(int i = 0;i <10;i++){
            threads[i].join();
            // 先执行子线程 后执行mian线程
        }
        System.out.println("main");
    }
}

结果:随机打印 0到9 ,后立刻打印 main

2.6 获取当前线程引用

方法

说明

public static

返回当前线程对象的引用

2.7 线程休眠

作用:让当前线程,休眠给定的时间。

注意:因为线程的调度是不可控的,所以,这个方法只能保证线程真正休眠时间是大于等于休眠时间的

方法

说明

public tatic

休眠当前线程 millis 毫秒

public static

同理,但休眠的时间精确度更高

2.8 其他方法

方法

说明

public static

返回当前线程的thread group及其子组中活动线程数的估计

(线程+线程创建的子线程存活的估计数量)

public final void setDaemon(boolean on)

将此线程标记为 daemon线程或用户线程

(true ,将此线程标记为守护线程 )

public static

使当前线程让出CPU

当前线程由运行态变为就绪态

守护线程/后台线程:非用户线程的线程为守护线程。无论守护线程是否存在,没有用户线程存活的进程会自动退出

2.9 补充

java中的内存区域

栈             每个线程都有自己独立的栈空间
                 栈里的数据不共享
                 
  堆             大家共享的是同一个堆(包括堆里的常量池)和方法区
  方法区          堆和方法区中的数据是共享的