java线程那些事

来源:互联网 发布:饭菜先生餐饮软件 编辑:程序博客网 时间:2024/05/19 12:17

一,daemon线程与非daemon线程有哪些区别?

       daemon线程顾名思义是后台执行的线程,但不是程序是否可以运行的关键因素。当JVM检测到系统中没有一个non daemon线程时,JVM就会退出;只要还有一个non daemon线程再运行,JVM就不会退出,daemon线程也就可以继续执行下去。因此main线程一定是一个non daemon线程。

       new Thread()构造函数创建线程的时候,会调用init()方法,从里面可以看出,新产生的线程会继承父线程的daemon属性和线程优先级。

       daemon属性和线程的优先级是两个正交的概念。

       详细讨论参见 daemon vs non daemon   daemon vs priority


二,调用线程的Thread.interrupt()方法以后会产生什么后果?

       Java中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理中断。这好比是家里的父母叮嘱在外的子女要注意身体,但子女是否注意身体,怎么注意身体则完全取决于自己。类似于操作系统中的非抢占式调度一样,只是一个提示作用,被中断的线程可以处理中断情况也可以完全忽略。

        中断的实现机制如下:

The interrupt mechanism is implemented using an internal flag known as the interrupt status. Invoking Thread.interrupt sets this flag. When a thread checks for an interrupt by invoking the static method Thread.interrupted, interrupt status is cleared. The non-static Thread.isInterrupted, which is used by one thread to query the interrupt status of another, does not change the interrupt status flag.

      调用Thread.interrupt()方法以后,被中断的线程会进行如下处理,摘自jdk文档

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

If this thread is blocked in an I/O operation upon an InterruptibleChannel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a java.nio.channels.ClosedByInterruptException.

If this thread is blocked in a java.nio.channels.Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked.

If none of the previous conditions hold then this thread's interrupt status will be set.

Interrupting a thread that is not alive need not have any effect

      翻译成中文就是:

      1,如果被中断的线程处于wait(), wait(long), wait(long, int), join(), sleep() 等申明了可以抛出InterruptedException()的方法,则会收到 InterruptedException(), 且中断标志会被清除。

      2,如果线程阻塞在一个 InterruptibleChannel的IO操作上,该channel会被关闭,线程中断状态会被设置,且线程会收到ClosedByInterrupteException异常。

      3,如果线程阻塞在Selector上,则线程中断状态会设置,且会马上从阻塞中退出,获得一个非0的返回结果。

      4,如果都不是上述3中情况,则线程中断状态会设置,但是线程还是可以正常执行下去,也可以检测或者不检测中断状态,如果不处理中断状态,那么一旦执行到前面的3种代码则会触发上面的规则。

三,线程池的作用

    线程池技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。    

    假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。


    如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。

四,线程池的基本组成部分有哪些?

    1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;

    2、工作线程(WorkerThread):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;

     3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;

     4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

     5 任务分配器:将提交的任务分配给工作线程执行,java中的实现策略是让工作线程自动的去任务队列中去获取任务,这样就不需要任务分配器了。

五,java的线程池的默认线程工厂是如何实现的?

      

    static class DefaultThreadFactory implements ThreadFactory {        private static final AtomicInteger poolNumber = new AtomicInteger(1);        private final ThreadGroup group;        private final AtomicInteger threadNumber = new AtomicInteger(1);        private final String namePrefix;        DefaultThreadFactory() {            SecurityManager s = System.getSecurityManager();            group = (s != null) ? s.getThreadGroup() :                                  Thread.currentThread().getThreadGroup();            namePrefix = "pool-" +                          poolNumber.getAndIncrement() +                         "-thread-";        }        public Thread newThread(Runnable r) {            Thread t = new Thread(group, r,                                  namePrefix + threadNumber.getAndIncrement(),                                  0);            if (t.isDaemon())                t.setDaemon(false);            if (t.getPriority() != Thread.NORM_PRIORITY)                t.setPriority(Thread.NORM_PRIORITY);            return t;        }    }

             从代码中可以看出,默认线程工厂主要是对产生的线程做了如下处理:

         1,对线程名字的前缀做了处理,模式如下pool-m-thread-n

         2,   将线程设置成后台模式

         3,将线程的优先级调整为普通优先级。

六,线程池中的参数corePoolSize, maximumPoolSize, blockQueue的关系是怎么样的?

      1,线程池刚创建的时候是没有线程的。

      2,poolSize < corePoolSize时,新提交的task会生成一个新的线程进行处理。

      3,poolSize >= corePoolSize,则尝试将task放入blockQueue中,如果成功放入则返回,否则转到步骤4。

      4,poolSize >= corePoolSize,且无法将task放入队列中,如果poolSize < maximumPoolSize, 则生成新的线程来处理task,否则调用RejectedExecutionHandle来处理任务。

七,常见的RejectedExecutionHandle有哪些处理策略?

      1,AbortPolicy 抛出 RejectedExecutionException异常,这个也是jdk线程池的默认实现。

      2,CallerRunsPolicy 由调用方来完成任务的执行,对调用方来说相当于自己串行执行了这个task。

      3,DiscardPolicy 直接吞掉这个任务,没有任何提示。

      4,DiscardOldestPolicy 将任务队列中最老的任务丢弃掉,然后尝试将该任务加入,仿照第六步的策略,循环往复直到该任务能加入到队列中为止。


八, Java的线程池实现是如何同时处理Runnable 和 Callable<V>的?

       Java使用一个FutureTask<V>来包装了Runnable和Callable<V>, 使得最终需要实现的接口就是Runnable。


九, JDK的线程池是如何实现shutdown()方法的?

关闭线程池会将线程池的状态设置成SHUTDOWN, 这会阻止新的任务添加到线程池中, 同时如果任务队列为空且还有Worker在运行,则当Worker调用getTask()获取任务时会返回null,使得worker正常退出。如果任务队列里面还有任务,则会继续执行任务,并最后退出。

public void shutdown() {        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            checkShutdownAccess();            advanceRunState(SHUTDOWN);            interruptIdleWorkers();            onShutdown(); // hook for ScheduledThreadPoolExecutor        } finally {            mainLock.unlock();        }        tryTerminate();    }    public List<Runnable> shutdownNow() {        List<Runnable> tasks;        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            checkShutdownAccess();            advanceRunState(STOP);            interruptWorkers();            tasks = drainQueue();        } finally {            mainLock.unlock();        }        tryTerminate();        return tasks;    }

   

从上面的代码可以看出来,shutdown() 与 shutdownNow()的工作流程类似,只不过一个将线程池状态设置成SHUTDOWN, 另外一个会设置成STOP, 并且会把在任务队列里面的任务取出来返回。


关闭线程池需要关闭里面的工作线程Worker, 是通过中断机制来唤醒在等待空的任务队列里面的Worker的,具体的实现代码如下:

   

private void interruptIdleWorkers(boolean onlyOne) {        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            for (Worker w : workers) {                Thread t = w.thread;                if (!t.isInterrupted() && w.tryLock()) { // 线程没有被中断且没有执行task,在执行task之前Worker会获取执行锁,内部其实是使用AQS来实现无锁算法。                    try {                        t.interrupt();                    } catch (SecurityException ignore) {                    } finally {                        w.unlock();                    }                }                if (onlyOne)                    break;            }        } finally {            mainLock.unlock();        }    }


下面是Worker线程获取任务的方法,如果返回null,则工作线程会退出。

   

    private Runnable getTask() {        boolean timedOut = false; // Did the last poll() time out?        for (;;) {            int c = ctl.get();            int rs = runStateOf(c);            // Check if queue empty only if necessary.            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {                // shutdownNow()后会直接返回null,如果只是shutdown(),且任务队列为空,也会返回null,否则会取出任务并执行。                decrementWorkerCount();                return null;            }            int wc = workerCountOf(c);            // Are workers subject to culling?            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;            if ((wc > maximumPoolSize || (timed && timedOut))                && (wc > 1 || workQueue.isEmpty())) {                if (compareAndDecrementWorkerCount(c))                    return null;                continue;            }            try {                Runnable r = timed ?                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :                    workQueue.take();                if (r != null)                    return r;                timedOut = true;            } catch (InterruptedException retry) {             // shutdown后会设置每一个Worker的interrupted属性,将等待任务队列的线程唤醒,由于这个时候任务队列是空的,Worker会从循环开始的检测条件中返回null并结束。                timedOut = false;            }        }    }

1 0
原创粉丝点击