Thread 类的基本用法

目录

一. 线程创建

1.继承Thread类 重写run().

2.通过实现Runnable 来实现创建线程.

3.使用匿名内部类, 来创建 Thread 子类.

4. 使用匿名内部类, 实现Runnable接口.

5. lambda 表达式

二. 线程中断

1. 什么是线程中断?

2.定义标志位

1.自己定义

2.使用标准库自带的标志位

三. 线程等待

四. 线程休眠

五. 获取线程实例


一. 线程创建

1.继承Thread类 重写run().

class MyThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("thread");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Demo1 {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();

        while (true) {
            System.out.println("main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2.通过实现Runnable 来实现创建线程.

class MyRunnable implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Demo2 {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();


        while (true) {
            System.out.println("main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

 这里的话, 就把线程要干的活和线程本身分开了, 使用 runnable 专门来表示 "线程要完成的工作", 把任务提取出来, 就是为了 解耦合 , 上面继承 Thread 的写法就是把线程要完成的工作和线程本身, 耦合在一起了. 而 runnable 这种写法, 只需要把 runnable 传给其他实体就可以了.

3.使用匿名内部类, 来创建 Thread 子类.

// 使用匿名内部类, 来创建 Thread 子类
    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        
        thread.start();
    }

4. 使用匿名内部类, 实现Runnable接口.

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        thread.start();
    }

5. lambda 表达式

    public static void main(String[] args) {
        Thread thread = new Thread( () -> {
            while (true) {
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread.start();
    }

二. 线程中断

1. 什么是线程中断?

看看API中的说法

线程的 thread.interrupt() 方法是中断线程,将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true).

run() 方法执行完了, 线程就结束了. 而线程中断就可以让线程提前结束.

(本质上, 是让线程尽快结束, 不是执行到一半就结束)

2.定义标志位

1.自己定义

直接自己定义一个标志位, 作为线程是否结束的标志

    private static boolean isQuit = false;

    public static void main(String[] args) {
        Thread thread = new Thread( () -> {
            while (!isQuit) {
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行结束");
        });

        thread.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        isQuit = true;
        System.out.println("设置让thread线程结束!");
    }

运行结果如下

thread打印三次后, 线程结束. 这就是我们自己设置标志位使得线程结束.

2.使用标准库自带的标志位

    public static void main(String[] args) {
        Thread thread = new Thread( () -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行结束");
        });

        thread.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread.interrupt();
        System.out.println("设置让thread线程结束!");
    }

 currentThread() 是Thread类的静态方法, 通过这个方法, 就可以拿到当前线程的实例. (也就是拿到当前线程对应的Thread对象).

isInterrupted()  这个方法是在判定标志位.

 在主线程中, 通过thread.interrupt() 来中断线程, 设置标志位为true.

打印结果如下:

 可以看到先输出了设置线程结束, 但是线程并没有结束, 而且抛出了异常, 然后继续打印. 这是因为interrupt方法的行为有两种情况.

        1. thread线程在运行状态, 会设置             标志位为true.

        2. 而当thread线程在阻塞状态(也就是sleep的时候), 不会设置标志位, 而是触发一个                              InterrupturedException, 这个异常会把 sleep 提前唤醒, 但是由于代码中, 只是打印了日志,             并没有结束循环, 因此循环还在继续执行. 要想顺利结束循环, 只需要加上 break 就好.

           

 并且, 在 Java 中, 中断线程并非是强制的, 而是由线程自身的代码来进行判定的. 有如下三种方法:

三. 线程等待

我们知道线程的调度顺序, 是不确定的!!!  

我们可以通过一些特殊操作, 来对线程的执行顺序, 做出人为干预. 其中 join, 就是一种办法, 来控制线程之间的结束顺序.

在 main 中调用 thread.join 的作用就是让 main 线程阻塞等待, 等到 thread 执行完之后, main 才开始执行.

    public static void main(String[] args) {
        Thread thread = new Thread( () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }) ;

        thread.start();

        System.out.println("main线程join之前");
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main线程join之后");
    }

注意join这里也是要出异常的.

 根据上图我们可以看到,  main线程在 join 之前先打印了, 然后 thread 循环打印五次之后, 才打印后面的那句话, 这就说明在打印五次 thread 之间, mian线程在join这里是阻塞的, 五次执行完之后吗才开始执行main线程.

这里就是 main线程进入阻塞, 不参与CPU的调度了, 而 thread 继续参与调度, 所以 thread 可以继续执行.

还有一个问题?

关于下图方框中的两个谁先打印的问题?

 首先, 我们可以肯定说这是不确定的. 主要还是看系统的调度, 上面我们已经说了, 系统的调度顺序是不确定的, 但是, 即使如此, 我们大概率看到先打印出来的还是main.

为什么呢?
线程 start 之后, 内核创建线程也是有开销的, start 调用的时候, 为了执行新线程的代码, 需要做一些准备工作, 这里需要消耗时间.

join还有其他几种方法, 带时间的版本, 意思就是等一定的时间, 时间一到就不等了,就像我们出去玩和朋友约定了时间在哪里碰头, 到点了人还没来, 咱就说再等个十分钟啥的, 等不到就走了, 而不带时间参数的就是一直等

四. 线程休眠

线程休眠, 也就是上面代码中多次出现的 sleep() , sleep就是指定休眠的时间, 让线程阻塞一会, 操作系统管理这些, 线程的 PCB 的时候, 是有多个链表的. 

 首先我们需要知道, 就绪队列的 PCB 才会参与到调度中. 假设1这里调用了 sleep , 则这个PCB就会被移动到阻塞队列中, 而当 sleep 的时间一到, 那么1号线程就会移动到之前的就绪队列里面.

 

五. 获取线程实例

在操作系统中, 对于 PCB 有一个状态的描述. 但是 Java中有一套自己的状态规则.

  • NEW : Thread 对象被创建出来, 但是内核的PCB还没创建(还没真正的创建线程)
  • TERMINATED : 内核的PCB销毁了, 但是Thread对象还在
  • RUNNABLE : 就绪状态. (正在CPU上运行 + 在就绪队列中排队)
  • TIMED_WAITING : 按照一定的时间, 进行阻塞, sleep.
  • WAITING : 特殊的阻塞状态, 调用wait.
  • BLOCKED : 等待锁的时候进入的阻塞状态.

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // t 开始之前, 得到的就是 NEW
        System.out.println(t.getState());

        t.start();

        Thread.sleep(50);

        // t 正在工作, 得到的是 RUNNABLE
        System.out.println(t.getState());

        t.join();

        // t 结束之后, 得到的状态 TERMINATED
        System.out.println(t.getState());
    }

 

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

即将秃头的菜鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值