美文网首页
Java Thread知识点总结

Java Thread知识点总结

作者: 沉淀之际 | 来源:发表于2019-05-20 15:58 被阅读0次

简介

什么是线程

进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
线程:一个线程不能独立的存在,它必须是进程的一部分。

线程的生命周期
生命周期

实现方式

大家可能在面试过程中、文章中或者书上看到过“实现线程有几种方式?”这样的问题,基本都是千篇一律的说两种方式:1、继承Thread重写run方法;2、实现Runnable接口。其实这里是有问题的,总所周知在Java中使用Thread表示线程的,所以实现Thread只有一种方式:那就是构造Thread类。而实现线程的执行单元有两种方式:1、继承Thread重写run方法;2、实现Runnable接口的run方法,并将Runnable实例作为构造Thread的参数。

API讲解

构造函数
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(String name) {
        init(null, null, name, 0);
    }

    public Thread(ThreadGroup group, String name) {
        init(group, null, name, 0);
    }

    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }

    public Thread(ThreadGroup group, Runnable target, String name) {
        init(group, target, name, 0);
    }

    public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
        init(group, target, name, stackSize);
    }
    
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
    ...
    }

强烈建议:在构造线程的时候为线程取一个有特殊意义的名字,有助于问题的排查和线程的跟踪(特别是在线程比较多的程序中)。

Thread.sleep和TimeUnit

sleep()方法导致程序暂停执行指定的时间,让出cpu给其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。

sleep是一个静态方法,有两个重载方法,其中一个需要传入毫秒数,另一个既需要毫秒数也需要纳秒数。

public static void sleep(long millis)
public static void sleep(long millis, int nanos)

TimeUnit是JDK1.5以后引入的,其对sleep有更好的封装性,能更好地、更精准地控制。

TimeUnit.HOURS.sleep(1);
TimeUnit.MUNUTES.sleep(1);
TimeUnit.SECONDS.sleep(1);

强烈建议:用TimeUnit代替Thread.sleep,因为sleep能做的事,TimeUnit都能做并且功能更强大。

Thread.yield

yield方法属于一种启发式的方法,其会提醒调度器我愿意放弃当前CPU资源。如果CPU资源不紧张,则会忽略这种提醒。如果生效,会使当前线程从Running状态切换到Runnable状态。

new Thread(() -> {
    Thread.yield();
    System.out.println("yield");
  }
).start()
yield和sleep区别
  • sleep会导致线程暂停指定时间,会使线程处于block状态;yield如果CPU调度器没有忽略,会导致线程处于Runnable状态。
  • sleep几乎百分之百地完成给定时间的休眠;yield并不能保证成功。
  • 一个线程sleep,另一个线程调用interrupt会捕获到中断信号;yield不会。
join

join()方法是Thread类中的一个方法,该方法的定义是等待该线程终止。其实就是join()方法将挂起调用线程的执行,直到被调用的对象完成它的执行。join与sleep一样是一个可中断的方法。

ThreadTest test  =new ThreadTest();
ThreadTest test2  =new ThreadTest();
test.setName("one");
test2.setName("two");
Thread t1 = new Thread(test);
Thread t2 = new Thread(test2);
t1.start();
/**
* 主线程向下转时,碰到了t1.join(),t1要申请加入到运行中来,就是要CPU执行权。
* 这时候CPU执行权在主线程手里,主线程就把CPU执行权给放开,陷入冻结状态。
* 活着的只有t1了,只有当t1拿着执行权把这些数据都打印完了,主线程才恢复到运行中来
*/
//join 方法 确保 t1执行之后 执行t2
t1.join();
t2.start();
join和CountDownLatch区别
  • 都适用“主程序中需要等待所有子线程完成后 再继续任务”的场景
  • 调用join方法需要等待thread执行完毕才能继续向下执行,而CountDownLatch只需要检查计数器的值为零就可以继续向下执行,相比之下,CountDownLatch更加灵活一些,可以实现一些更加复杂的业务场景。
interrupt、interrupted、isInterrupted
interrupt

interrupt()方法: 作用是中断线程。

  • 本线程中断自身是被允许的,且"中断标记"设置为true
  • 其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。
  • 若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。
    例如:线程通过wait()、sleep()、join()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。
    如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。
  • 如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。
interrupted和isInterrupted
  • interrupted和isInterrupted都是判断当前线程是否被中断
  • interrupted会清除掉线程的interrupt标志;isInterrupted不会
        System.out.println("Main Thread is interrupted? " + Thread.interrupted());
        Thread.currentThread().interrupt();
        System.out.println("Main Thread is interrupted? " + Thread.currentThread().isInterrupted());
//        System.out.println("Main Thread is interrupted? " + Thread.interrupted());
        try {
            TimeUnit.MINUTES.sleep(1);
        } catch (InterruptedException e) {
            System.out.println("I will be interrupted still.");
        }

思考:打开注释和关闭注释的区别?

如何关闭一个线程
  • 正常关闭
    1、 线程正常结束,线程自动退出
    2、捕获中断信号
    3、适用volatile开关控制
  • 异常退出
    补充:Thread API中包含了一个stop()方法,可以突然终止线程。但它在JDK1.2后便被淘汰了,因为它可能导致数据对象的崩溃。一个问题是,当线程终止时,很少有机会执行清理工作;另一个问题是,当在某个线程上调用stop()方法时,线程释放它当前持有的所有锁,持有这些锁必定有某种合适的理由——也许是阻止其他线程访问尚未处于一致性状态的数据,突然释放锁可能使某些对象中的数据处于不一致状态,而且不会出现数据可能崩溃的任何警告。
守护线程

若JVM中没有一个非守护线程时,JVM的进行会退出。
守护线程具备自动结束生命周期的特性,经常用于执行一些后台工作,因此有时它也被称为后台线程。

//伪代码如下
Thread thread = new Thread();
thread.setDaemon(true);
thread.start()

相关文章

网友评论

      本文标题:Java Thread知识点总结

      本文链接:https://www.haomeiwen.com/subject/vtvhzqtx.html