Java线程核心基础(上)

Java线程核心基础(上)

1、实现多线程

  根据Oracle官方文档,目前推荐的建立线程方法主要有两种,分别是继承Thread类和实现Runnable接口。经过阅读Thread类源码,能够发现继承Thread类须要重写run()方法,而实现Runnable接口会将本身实现的对象在new Thread()时,经过Thread构造函数传给Thread类中的target对象,并在调用run()方法时调用target.run(),下面让咱们看源码。java

/* 
    What will be run. 这是Thread类中的target对象
*/
private Runnable target;
/* 
    当调用run()方法时会判断target是否为空,
    若是是继承Thread类run()方法被重写,就不会执行如下代码了
*/
   @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

  那么这两种方法哪个更好呢? 实现Runnable接口更好,因为Java是单继承类但能够实现多个接口,若是继承了Thread类后续因为业务须要就不能继承新的类了,而实现Runable接口就没有这个问题。另外对于线程池,Callable,FutureTask,定时器,匿名内部类,lambda表达式等其余能够建立线程的方法,究其本质只是对以上两种方法进行了包装。多线程

  若是同时实现了两种方法会发生什么?即既传入Ruable对象,又重写run()方法。答案是会调用重写的run()方法,根据面向对象思想,子类重写父类方法,则父类原方法就没法调用了,target.run()也就没法执行了。ide

  最后对这两种实现线程的方式一句话总结:一种建立线程的方式,两种实现执行单元的方式。函数

2、start() 和 run()方法的比较

  start()方法能够启动新线程,并作准备工做,start()方法不能重复调用,会在第二次调用时抛出IIegalThreadStateException()。下面看一下源码this

 

// 线程状态默认未启动
private volatile int threadStatus = 0;

public synchronized void start() {
        // 判断线程是否已启动,已启动则抛出异常
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        // 加入线程组
        group.add(this);

        boolean started = false;
        try {
            // 调用native方法建立线程
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }    

  run()方法直接调用就是普通方法,不会建立新线程执行,只用使用start()方法间接调用run()方法才能在新线程中执行。线程

3、如何正确中止线程

  这个内容很是重要,中止线程应使用interrupt来通知,而不知强制中止。如何使用interrupt来请求中止线程呢? 有如下几种状况:对象

    1. 普通状况run() 方法中没有sleep() 或wait()方法时,可使用isInterrupted()方法进行判断。 ( 关于isInterrupted() 和 inInterrupted() 的区别后面会讲到)blog

    2. 线程可能阻塞的状况, 当线程阻塞时收到interrupt中断会当即抛出异常响应中断,线程结束继承

    3. 若是线程在每次工做迭代以后都阻塞,能够在迭代外层try/catch捕获异常并中断线程,若是在迭代内try/catch捕获异常,线程没法中止,由于sleep()或wait()方法会把interrupt标记位清除。接口

  在实际生产开发过程当中,对于中止线程的最佳的处理方式: 

    1. 优先选择: 传递中断

    2. 不想或没法传递:恢复中断

    3.不该屏蔽中断

  错误的处理方式:在方法中吞掉中断。 可将异常抛到顶层在run()方法中处理。

  另外,错误中止线程的方法

  1. 被弃用的stop(), suspend()和resume() 方法, 使用stop()会使线程戛然而止,致使线程不能进行最后的收尾工做,可能对系统形成损害。 suspend()会挂起线程可是不会释放锁,可能会形成死锁。

  2. 使用volatile设置Boolean标记位,这个方法相信不少人都会怀疑,啊?这个也是错误的?其实这个方法错就错在,虽然volatile能保证标记位对于线程随时可见,可是当线程阻塞时,是没法检查标记位的,若是没有其它线程唤醒,则阻塞线程会进入永久阻塞。 正确方法仍是用interrupt()来通知要中止的线程。

4、线程的生命周期

  线程总共有六个状态,New 已建立但还还没有启动的新线程,Runnable可运行,Blocked被阻塞,Waiting等待,Timed Waiting限期等待,Terminated终止。 通常而言会把 Blocked,Waiting,Timed Waiting都称为阻塞状态。 

     

相关文章
相关标签/搜索