多线程学习之线程的创建与终止

什么是线程

现代操作系统调度的最小单元是线程,也叫轻量级进程,在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。

为什么要使用多线程

(1)更多的处理器核心
(2)更快的响应时间
(3)更好的编程模型

线程生命周期

在这里插入图片描述
线程创建之后,调用start()方法开始运行。当线程执行wait()方法之后,线程进入等待状态。进入等待状态的线程需要依靠其他线程的通知(notify)才能够返回到运行状态,超时等待状态相当于在等待状态的基础上增加了超时限制,也就是超时时间到达时将会返回到运行状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到阻塞状态。线程在执行Runnable的run()方法完之后将会进入到终止状态。
阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
但利用Lock接口进行同步的线程状态却是等待状态,因为Lock接口对于阻塞的实现均使用了LockSupport类中的相关方法而不是synchronized。

线程的几种创建模式

通过实现Runnable接口来创建Thread线程:

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

    }
    static class MyThread implements Runnable {
        @Override
        public void run() {
            while (true){
                System.out.println("MyThread is ruing");
            }
        }
    }
}

控制台输出

MyThread is ruing
MyThread is ruing
MyThread is ruing

通过继承Thread类来创建一个线程:

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

    }
    static class MyThread2 extends Thread {
        @Override
        public void run() {
            while (true){
                System.out.println("MyThread2 is ruing");
            }
        }
    }

控制台输出

MyThread2 is ruing
MyThread2 is ruing
MyThread2 is ruing

通过实现Callable接口来创建Thread线程

public static void main(String[] args) {
    FutureTask<Integer> future = new FutureTask(new MyCallable());
    Thread thread = new Thread(future);
    thread.start();
    try {
        /**
         * get()返回Callable任务里的call()返回值
         * get方法是一个阻塞方法,对于task内置了一些任务状态,当任务状态为新建0或者初始化完成的时候1的时候会阻塞
         * 需要根据设置的时间阻塞,没有设置时会一直进行阻塞,一直到有结果返回
         */
       Integer result = future.get();
        System.out.println("线程运行结果"+result);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
static class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws InterruptedException {
        int i = 0;
        int sum = 0;
        while(i<10){
            Thread.sleep(100);
            sum+=i;
            System.out.println(i);
            i++;
        }
        return sum;
    }
}

控制台输出

0
1
2
3
4
5
6
7
8
9
线程运行结果45

创建线程的三种方式的对比

  • 实现Runnable、Callable接口
    优点:线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。可以多个线程可以共享同一个target对象,一起执行,适合多个线程来处理同一份资源的情况。

  • 继承Thread类的
    优点:编写简单,直接使用this即可获得当前线程。
    缺点:线程类已经继承了Thread类,所以不能再继承其他父类。

  • Runnable和Callable的区别
    (1) Callable重写的方法是call(),Runnable重写的方法是run()。
    (2) Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
    (3) call方法可以抛出异常,run方法不可以。
    (4) 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
    在这里插入图片描述
    Future接口的结构
    在这里插入图片描述

  • boolean cancel(boolean mayInterruptIfRunning);
    尝试取消执行此任务。如果任务已经完成,已经被取消或其他原因而无法取消,则失败。
    如果成功,并且此任务尚未开始,则此任务永远不会运行。
    如果任务已经开始,则参数确定是否应中断执行该任务的线程以尝试停止该任务。
    调用这个方法返回后,isDone方法将始终返回true。
    如果此返回了true,则 isCancelled方法将始终返回true。

  • boolean isCancelled();
    如果此任务在正常完成之前被取消,则返回true

  • boolean isDone();
    如果此任务完成,则返回 true。
    由于正常终止,异常或取消引起的,此方法都将返回true

  • V get() throws InterruptedException, ExecutionException;
    等待必要的计算完成,然后检索其结果。

  • V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    必要时最多等待给定时间以完成计算,然后检索其结果,如果超时则不等待。

守护线程

守护线程主要被用作程序中后台调度以及支持性工作。当一个Java虚拟机中不存在非守护线程的时候,Java虚拟机将会退出。
可以通过调用Thread.setDaemon(true)将线程设置为Daemon线程(需要在启动线程之前设置,不能在启动线程之后设置)。
守护线程不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。因为当主线程结束后,守护线程也会被结束。

终止线程

中断

中断可以理解为线程的一个标识位属性,表示一个运行中的线程是否被其他线程进行了中断操作,其他线程通过调用该线程的interrupt()方法对其进行中断操作。
线程通过调用自身方法isInterrupted()来进行判断是否被中断。如果线程已经处于终结状态,即使该线程被中断过,在调用该线程对象的isInterrupted()时依旧会返回false。
线程要对中断做出响应才能合理利用中断

private static class Runner implements Runnable {
    private long i;
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()){
            i++;
        }
        System.out.println("Count i = " + i);
        //进行资源释放等操作
    }
}

例如此时在其他线程调用此线程的interrupt(),那么在执行到Thread.currentThread().isInterrupted()跳出循环,释放资源后结束线程。
在终止线程时也不能直接武断的调用suspend()、resume()和stop()进行终止,在调用后,线程不会释放已经占有的资源(比如锁),而是占有着资源进入睡眠状态,这样容易引发死锁问题。同样,stop()方法在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会。
**应该在任务处理时就设计好响应中断,利用中断来做终止线程的工作,当判断中断时就跳出循环,结束线程 **

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值