精华内容
下载资源
问答
  • Java线程池学习 ThreadLocal学习 实战bug现场show 1. Java线程池学习 Doug Lea大神的工厂类中的实现 1.1 常用线程池介绍 固定线程池(Executors.newFixedThreadPool):线程数为一个固定的值,当超出时放入链表...

    目录

    1. Java线程池学习
    2. ThreadLocal学习
    3. 实战bug现场show

    1. Java线程池学习

    Doug Lea大神的工厂类中的实现

    1.1 常用线程池介绍

    1. 固定线程池(Executors.newFixedThreadPool):线程数为一个固定的值,当超出时放入链表队列,FIFO先进先出,队列满时直接抛出拒绝异常。参数:core,max线程数均一致为入参的NThread指定的数量,keepAliveTime为0L,队列为LinkedBlockingQueue链表阻塞队列,默认的AbortPolicy策略(即直接抛出拒绝异常:RejectedExecutionException);
    2. 单线程线程池(Executors.newSingleThreadExecutor):只有一个线程执行,其余入队列;core,max均为1,其余同Fixed
    3. 缓存线程池(Executors.newCachedThreadPool):线程数会一直增长,直到Integer最大值之后,再向同步队列中添加元素,同步队列满了则抛出拒绝异常。参数:core为0,max为Integer最大值,keepAliveTime为60秒,队列为同步队列SynchronousQueue

    1.2 ThreadPoolExecutor学习

    ThreadPoolExecutor构造参数介绍

    参数 参数类型 参数含义
    corePoolSize int 队列未满情况下的工作线程数
    maximumPoolSize int 队列满之后可以突破core配置达到的最大线程数
    keepAliveTime long 从队列中获取任务的等待超时时间,如果获取超时会退出Worker,如果允许core线程超时,那么会移除所有Worker,如果队列有任务则保留至少一个Worker线程
    unit TimeUnit 超时时间单位
    workQueue BlockingQueue 任务数超过core配置后要放入的队列
    threadFactory ThreadFactory 线程工厂
    handler RejectedExecutionHandler 队列满之后线程数超过max配置后拒绝任务的handle实现

    1.2.1 提交任务:execute(Runnable command)

    1. Worker的数量小于core配置,则添加一个Worker:addWorker,firstTask为command,core为true
    2. Worker的数量超过core配置,并且线程池处于running状态,提交任务至队列;如果提交成功,再次检查是否是running状态,如果不是则试图从队列中移除该任务并回调reject;如果依然是running状态,并且Worker数量为0,(即一瞬间提交的任务超过了core配置,并且在超过的那一刻,所有的core任务全部执行完毕),则继续添加addWorker,firstTask为null,core为false,(即将刚刚添加至队列的任务拉取出来并执行)
    3. Worker的数量超过core配置,并且队列已满,继续添加Worker,firstTask为command,core为false,(此时会突破core配置继续添加Worker数至max线程配置);如果添加失败则回调reject拒绝任务(即超出了队列并且所有的工作线程数达到max配置均在执行)

    1.2.2 添加Worker:addWorker

    1. 如果是core任务,判断是否超过core配置,超过则返回false(即添加失败),否则递增worker数量
    2. 如果不是core任务,判断是否超过max配置,超过则返回false(即添加失败),否则递增worker数量
    3. 将command封装为Worker添加至workers集合,执行worker
    4. 如果执行失败(如果没有执行失败,Worker的递减动作在getTask或者processWorkerExit(突然完成循环才会走该逻辑)中执行),移除worker,并递减worker数量(说明此时任务已经超出max配置)
    5. 尝试一次终止
    6. 如果线程池为running、tidying、terminated状态之一或者(shutdown状态并且队列不为空)直接返回
    7. Worker数量不为0,终止一个空闲Worker后返回,即遍历workers,调用与Worker绑定的线程的interrupt方法,onlyOne为true则break
    8. Worker数量为0,将线程池状态改为整理中tidying,执行terminated,将线程池状态改为terminated,唤醒termination条件的所有等待

    1.2.3 执行Worker:run(runWorker)

    1. 循环获取任务,如果与Worker绑定的command不为null则继续,如果为null则getTask(即从队列中获取command),如果getTask不为null则继续
    2. 如果线程池状态为Stop或者整理中tidying或者terminated,则调用interrupt终止任务,否则继续
    3. 执行beforeExecute
    4. 执行command任务
    5. 执行afterExecute
    6. task置为null,完成任务递增,继续循环
    7. 循环结束处理离开动作processWorkerExit
    8. 如果是突然的完成completedAbruptly=true,即没有正常的完成循环,则递减Worker数量
    9. 如果是正常的完成completedAbruptly=false,将完成任务数计入completedTaskCount属性,从workers中移除Worker
    10. 尝试一次终止:同上
    11. 如果线程池状态为running或shutdown,如果不是突然的完成,如果允许core线程超时allowCoreThreadTimeOut则min置为0,否则使用core配置,如果min为0并且workQueue不为空(如果为空说明所有的任务都已经在Worker中执行不需要再添加Worker去拉取队列中的任务了),则min置为1,如果Worker数量大于等于min则return返回,否则添加Worker。(即线程池为running或者shutdown状态,Worker数量小于最小值,则添加一个Worker拉取队列任务执行)。其实就是如果没有允许超时则始终保持core配置数量的Worker,如果允许超时则不需保持Worker,如果队列不为空则保持至少一个Worker即可
    12. addWorker读取队列任务,firstTask为null,core为false

    1.2.4 获取队列任务执行:getTask

    1. 死循环获取任务
    2. 如果线程池大于等于shutdown状态并且(大于等于stop状态或者队列为空),递减Worker数量并返回null
    3. 如果允许core线程超时allowCoreThreadTimeOut为true或者Worker数量大于core配置则timed标识置为true
    4. (如果Worker数量大于max配置或者(timed与timeOut为true)),并且(Worker数量大于1或者队列为空),递减Worker数量并返回null;
    5. 如果Worker数量大于max并且Worker大于1或者队列为空,Worker突破限制理应销毁,则递减后返回null,即移除当前Worker(可能会再添加新得Worker)
    6. 如果允许超时(timed或者超过了core配置)并且从队列中获取任务超时(从队列中获取任务超过keepAliveTime配置),走到getTask方法说明Worker的本身绑定的firstTask已经执行完成,并且当前队列中获取任务已经超时,并且Worker数量大于1,即时队列不为空销毁当前的超时Worker也不会影响队列的消费,因为Worker数量大于1还存在另外至少一个Worker会尝试消费队列,递减后返回null,移除当前Worker(可能会再添加新得Worker)
    7. 如果允许超时则按照超时配置拉取队列中的任务,否则阻塞的形式一直等待直到获取到队列中的任务
    8. 如果获取的任务不为null返回,如果出现异常timeOut置为false(即继续重试),否则说明已经超时,timeOut置为true继续循环

    2. ThreadLocal学习

    1. 设置数据:set
    2. 获取当前线程
    3. 获取线程的ThreadLocalMap属性,
    4. 如果ThreadLocalMap不为空则按照当前实例为key设置value值
    5. 如果ThreadLocalMap为空则创建并设置value值
    6. 获取数据:get
    7. 获取当前线程
    8. 获取线程的ThreadLocalMap属性,
    9. 如果ThreadLocalMap不为空则按照当前实例为key读取map中的value值
    10. 如果ThreadLocalMap为空则设置初始化值并返回:setInitialValue,默认为null
    11. ThreadLocalMap结构
    12. ThreadLocalMap的entry的可以是对ThreadLocal实例的一个弱引用,value即设置的值

    1

    3. 大型bug现场show

    1. 问题描述
    2. 接口服务中使用ThreadLocal记录了整个流程上下文的一个Boolean标识符,在测试期间,刚启动时业务流程正常,一段时间后发现改标识符一直处于true状态,于是展开了地网式搜查
    3. 原因定位
    4. 首先第一步先查看日志,发现接口业务处理线程始终使用的是同一个Worker线程,ThreadLocal每次绑定的都是同一个Worker线程,那么有可能是没有清除ThreadLocal导致的,检查代码发现确实是如此
    5. 那么为什么会一直是同一个Worker线程呢?我们业务中使用的dubbo框架,于是看了我们业务代码中线程池的配置,发现使用的是SynchronousQueue的堆栈结构,由于测试期间没有并发每次都是一个一个串行的测试请求到接口,所以就出现了每次业务处理线程都与同一个Worker绑定的现象
    6. 如下图
    7. step1:3个固定的Worker阻塞在getTask等待同步队列中的数据
    8. step2:当一个业务线程启动便有栈顶Worker3获取到进行处理
    9. step3:处理完成后Worker3的getTask任务再次回到栈顶
    10. 2
    11. 解决方法
    12. ThreadLocal使用的风险可以通过监控来避免此类问题的再次出现
    13. 在使用完之后及时清理ThreadLocal缓存
    展开全文
  • 1.jdbc连接数据库,就这样子Class.for...java.sql.Connection conn = DriverManager.getConnection(jdbcUrl);2.通过传入jdbc url用Drivermanager.getConnection(jdbcurl)连接数据库,注意:一次Drivermanager.getCo...

    1.jdbc连接数据库,就这样子

    Class.forName("com.mysql.jdbc.Driver");

    java.sql.Connection conn = DriverManager.getConnection(jdbcUrl);

    2.通过传入jdbc url用Drivermanager.getConnection(jdbcurl)连接数据库,

    注意:一次Drivermanager.getConnection(jdbcurl)获得只是一个connection,并不能满足高并发情况。因为connection不是线程安全的,一个connection对应的是一个事物。

    3.所以数据库连接池,是多次Drivermanager.getConnection(jdbcurl),获取多个connection放入hashmap中。

    4.每次获得connection都需要浪费cpu资源和内存资源,是很浪费资源的。所以诞生了数据库连接池。

    5.数据库连接池部分源码:

    注意pool.getConnection(),都是先从threadlocal里面拿的,如果threadlocal里面有,则用,保证线程里的多个dao操作,用的是同一个connection,以保证事务。

    如果新线程,则将新的connection放在threadlocal里,再get给到线程。

    着重看以下几个方法,说明数据库连接池,是将connection放进threadlocal里的,以保证每个线程从连接池中获得的都是线程自己的connection。

    // 将线程和连接绑定,保证事务能统一执行

    成员变量   private static ThreadLocal threadLocal = new ThreadLocal();

    // 获得当前连接

    publicConnection getCurrentConnecton(){

    // 默认线程里面取

    Connection conn = threadLocal.get();

    if(!isValid(conn)){

    conn =getConnection();

    }

    returnconn;

    }

    // 获得连接

    publicsynchronized Connection getConnection() {

    Connection conn = null;

    try{

    // 判断是否超过最大连接数限制

    if(contActive < this.dbBean.getMaxActiveConnections()){

    if (freeConnection.size() > 0) {

    conn = freeConnection.get(0);

    if (conn != null) {

    threadLocal.set(conn);

    }

    freeConnection.remove(0);

    } else{

    conn =newConnection();

    }

    }else{

    // 继续获得连接,直到从新获得连接

    wait(this.dbBean.getConnTimeOut());

    conn =getConnection();

    }

    if(isValid(conn)) {

    activeConnection.add(conn);

    contActive ++;

    }

    } catch(SQLException e) {

    e.printStackTrace();

    } catch(ClassNotFoundException e) {

    e.printStackTrace();

    } catch(InterruptedException e) {

    e.printStackTrace();

    }

    returnconn;

    }

    public synchronized voidreleaseConn(Connection conn) throws SQLException {

    if (isValid(conn)&& !(freeConnection.size() >dbBean.getMaxConnections())) {

    freeConnection.add(conn);

    activeConnection.remove(conn);

    contActive --;

    threadLocal.remove();

    // 唤醒所有正待等待的线程,去抢连接

    notifyAll();

    }

    }

    然后再着重理解此段话

    首先,LZ是概念上的错误.什么是线程池,什么是ThreadLocal???线程池,为避免不必要的创建,销毁connection而存在的,其中包括活动,等待,最小等属性,cop3,proxy连接池都可以配置这些玩意;

    至于为什么要用ThreadLocal呢?这个和连接池无关,我认为更多的是和程序本身相关,为了更清楚的说明,我举个例子

    servlet中获取一个连接.首先,servlet是线程安全的吗?

    classMyServlet extends HttpServlet{privateConnection conn;

    }

    ok,遗憾的告诉你,这个conn并不是安全的,所有请求这个servlet的连接,使用的都是一个Connection,这个就是致命的了.多个人使用同一个连接,算上延迟啥的,天知道数据会成什么样.

    因此我们要保证Connection对每个请求都是唯一的.这个时候就可以用到ThreadLocal了,保证每个线程都有自己的连接.

    改为private ThreadLocal ct = new ThreadLocal();

    然后从连接池获取Connection,set到ct中,再get就行了,至于得到的是哪个Connection就是连接池的问题了,你也管不到.

    Hibernate的数据库连接池就是将connection放进threadlocal实现的!!!

    Hibernate的数据库连接池就是将connection放进threadlocal实现的!!!

    Hibernate的数据库连接池就是将connection放进threadlocal实现的!!!

    public classConnectionPool implements IConnectionPool {//连接池配置属性

    privateDBbean dbBean;private boolean isActive = false; //连接池活动状态

    private int contActive = 0;//记录创建的总的连接数//空闲连接

    private List freeConnection = new Vector();//活动连接

    private List activeConnection = new Vector();

    // 将线程和连接绑定,保证事务能统一执行

    private static ThreadLocal threadLocal = new ThreadLocal();

    publicConnectionPool(DBbean dbBean) {

    super();this.dbBean =dbBean;

    init();

    cheackPool();

    }//初始化

    public voidinit() {try{

    Class.forName(dbBean.getDriverName());for (int i = 0; i < dbBean.getInitConnections(); i++) {

    Connection conn;

    conn=newConnection();//初始化最小连接数

    if (conn != null) {

    freeConnection.add(conn);

    contActive++;

    }

    }

    isActive= true;

    }catch(ClassNotFoundException e) {

    e.printStackTrace();

    }catch(SQLException e) {

    e.printStackTrace();

    }

    }//获得当前连接

    publicConnection getCurrentConnecton(){//默认线程里面取

    Connection conn = threadLocal.get();if(!isValid(conn)){

    conn=getConnection();

    }returnconn;

    }//获得连接

    publicsynchronized Connection getConnection() {

    Connection conn= null;try{//判断是否超过最大连接数限制

    if(contActive < this.dbBean.getMaxActiveConnections()){if (freeConnection.size() > 0) {

    conn= freeConnection.get(0);if (conn != null) {

    threadLocal.set(conn);

    }

    freeConnection.remove(0);

    }else{

    conn=newConnection();

    }

    }else{//继续获得连接,直到从新获得连接

    wait(this.dbBean.getConnTimeOut());

    conn=getConnection();

    }if(isValid(conn)) {

    activeConnection.add(conn);

    contActive++;

    }

    }catch(SQLException e) {

    e.printStackTrace();

    }catch(ClassNotFoundException e) {

    e.printStackTrace();

    }catch(InterruptedException e) {

    e.printStackTrace();

    }returnconn;

    }//获得新连接

    privatesynchronized Connection newConnection()

    throws ClassNotFoundException, SQLException {

    Connection conn= null;if (dbBean != null) {

    Class.forName(dbBean.getDriverName());

    conn=DriverManager.getConnection(dbBean.getUrl(),

    dbBean.getUserName(), dbBean.getPassword());

    }returnconn;

    }//释放连接

    public synchronized voidreleaseConn(Connection conn) throws SQLException {if (isValid(conn)&& !(freeConnection.size() >dbBean.getMaxConnections())) {

    freeConnection.add(conn);

    activeConnection.remove(conn);

    contActive--;

    threadLocal.remove();//唤醒所有正待等待的线程,去抢连接

    notifyAll();

    }

    }//判断连接是否可用

    privateboolean isValid(Connection conn) {try{if (conn == null ||conn.isClosed()) {return false;

    }

    }catch(SQLException e) {

    e.printStackTrace();

    }return true;

    }//销毁连接池

    public synchronized voiddestroy() {for(Connection conn : freeConnection) {try{if(isValid(conn)) {

    conn.close();

    }

    }catch(SQLException e) {

    e.printStackTrace();

    }

    }for(Connection conn : activeConnection) {try{if(isValid(conn)) {

    conn.close();

    }

    }catch(SQLException e) {

    e.printStackTrace();

    }

    }

    isActive= false;

    contActive= 0;

    }//连接池状态

    @Overridepublicboolean isActive() {returnisActive;

    }//定时检查连接池情况

    @Overridepublic voidcheackPool() {if(dbBean.isCheakPool()){new Timer().schedule(newTimerTask() {

    @Overridepublic voidrun() {//1.对线程里面的连接状态//2.连接池最小 最大连接数//3.其他状态进行检查,因为这里还需要写几个线程管理的类,暂时就不添加了

    System.out.println("空线池连接数:"+freeConnection.size());

    System.out.println("活动连接数::"+activeConnection.size());

    System.out.println("总的连接数:"+contActive);

    }

    },dbBean.getLazyCheck(),dbBean.getPeriodCheck());

    }

    }

    }

    Why ThreadLocal?

    无论如何,要编写一个多线程安全(Thread-safe)的程序是困难的,为了让线程共享资源,必须小心地对共享资源进行同步,同步带来一定的效能延迟,而另一方面,在处理同步的时候,又要注意对象的锁定与释放,避免产生死结,种种因素都使得编写多线程程序变得困难。

    尝试从另一个角度来思考多线程共享资源的问题,既然共享资源这么困难,那么就干脆不要共享,何不为每个线程创造一个资源的复本。将每一个线程存取数据的行为加以隔离,实现的方法就是给予每个线程一个特定空间来保管该线程所独享的资源

    什么是ThreadLocal?

    顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。

    使用场景

    To keep state with a thread (user-id, transaction-id, logging-id)

    To cache objects which you need frequently

    ThreadLocal类  实现线程范围的共享变量

    它主要由四个方法组成initialValue(),get(),set(T),remove(),其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:

    ThreadLocal的原理

    ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:

    public class ThreadLocal

    {

    private Map values = Collections.synchronizedMap(new HashMap());

    public Object get()

    {

    Thread curThread = Thread.currentThread();

    Object o = values.get(curThread);

    if (o == null && !values.containsKey(curThread))

    {

    o = initialValue();

    values.put(curThread, o);

    }

    return o;

    }

    public void set(Object newValue)

    {

    values.put(Thread.currentThread(), newValue);

    }

    public Object initialValue()

    {

    return null;

    }

    }

    ThreadLocal 的使用

    使用方法一:

    hibernate的文档时看到了关于使ThreadLocal管理多线程访问的部分。具体代码如下

    1.  public static final ThreadLocal session = new ThreadLocal();

    2.  public static Session currentSession() {

    3.      Session s = (Session)session.get();

    4.      //open a new session,if this session has none

    5.   if(s == null){

    6.      s = sessionFactory.openSession();

    7.      session.set(s);

    8.   }

    return s;

    9. }

    我们逐行分析

    1。 初始化一个ThreadLocal对象,ThreadLocal有三个成员方法 get()、set()、initialvalue()。

    如果不初始化initialvalue,则initialvalue返回null。

    3。 session的get根据当前线程返回其对应的线程内部变量,也就是我们需要的net.sf.hibernate.Session(相当于对应每个数据库连接).多线程情况下共享数据库链接是不安全的。ThreadLocal保证了每个线程都有自己的s(数据库连接)。

    5。如果是该线程初次访问,自然,s(数据库连接)会是null,接着创建一个Session,具体就是行6。

    6。创建一个数据库连接实例 s

    7。保存该数据库连接s到ThreadLocal中。

    8。如果当前线程已经访问过数据库了,则从session中get()就可以获取该线程上次获取过的连接实例。

    使用方法二

    当要给线程初始化一个特殊值时,需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,EasyDBO中创建jdbc连接上下文就是这样做的:

    public class JDBCContext{

    private static Logger logger = Logger.getLogger(JDBCContext.class);

    private DataSource ds;

    protected Connection connection;

    private boolean isValid = true;

    private static ThreadLocal jdbcContext;

    private JDBCContext(DataSource ds){

    this.ds = ds;

    createConnection();

    }

    public static JDBCContext getJdbcContext(javax.sql.DataSource ds)

    {

    if(jdbcContext==null)jdbcContext=new JDBCContextThreadLocal(ds);

    JDBCContext context = (JDBCContext) jdbcContext.get();

    if (context == null) {

    context = new JDBCContext(ds);

    }

    return context;

    }

    private static class JDBCContextThreadLocal extends ThreadLocal {

    public javax.sql.DataSource ds;

    public JDBCContextThreadLocal(javax.sql.DataSource ds)

    {

    this.ds=ds;

    }

    protected synchronized Object initialValue() {

    return new JDBCContext(ds);

    }

    }

    }

    使用单例模式,不同的线程调用getJdbcContext()获得自己的jdbcContext,都是通过JDBCContextThreadLocal 内置子类来获得JDBCContext对象的线程局部变量

    本文部分转自http://blog.csdn.net/wenzhihui_2010/article/details/8985575

    展开全文
  • Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。 继承 Thread 类。 同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。 当调用 start() 方法启动一个线程时,虚拟...

    有三种使用线程的方法:

    • 实现 Runnable 接口;
    • 实现 Callable 接口;
      与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。
    • 继承 Thread 类。
      同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。
      当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。

    实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以理解为任务是通过线程驱动从而执行的。

    实现接口 VS 继承 Thread
    实现接口会更好一些,因为:

    Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
    类可能只要求可执行就行,继承整个 Thread 类开销过大。

    java线程的状态
    创建:当new了一个线程,并没有调用start之前,线程处于创建状态;
    就绪:当调用了start之后,线程处于就绪状态,这是,线程调度程序还没有设置执行当前线程;
    运行:线程调度程序执行到线程时,当前线程从就绪状态转成运行状态,开始执行run方法里边的代码;
    阻塞:线程在运行的时候,被暂停执行(通常等待某项资源就绪后在执行,sleep、wait可以导致线程阻塞),这是该线程处于阻塞状态;
    死亡:当一个线程执行完run方法里边的代码或调用了stop方法后,该线程结束运行

    Java 线程池
    事先维护一个线程池,根据需要取用,避免频繁地创建销毁线程
    设计一个动态大小的线程池:
    包含四个组成部分:
    线程池(ThreadPool)
    工作线程(Worker)
    任务接口(Task)
    任务队列(TaskQueue)

    Java线程池参数
    corePoolSize:线程池的大小。线程池创建之后不会立即去创建线程,而是等待线程的到来。当当前执行的线程数大于改值是,线程会加入到缓冲队列;
    maximumPoolSize:线程池中创建的最大线程数;
    keepAliveTime:空闲的线程多久时间后被销毁。默认情况下,改值在线程数大于corePoolSize时,对超出corePoolSize值得这些线程起作用。
    unit:TimeUnit枚举类型的值,代表keepAliveTime时间单位,可以取下列值:
    TimeUnit.DAYS; //天
      TimeUnit.HOURS; //小时
      TimeUnit.MINUTES; //分钟
      TimeUnit.SECONDS; //秒
      TimeUnit.MILLISECONDS; //毫秒
      TimeUnit.MICROSECONDS; //微妙
      TimeUnit.NANOSECONDS; //纳秒
    workQueue:阻塞队列,用来存储等待执行的任务,决定了线程池的排队策略,有以下取值:
      ArrayBlockingQueue;
      LinkedBlockingQueue;
      SynchronousQueue;
      五、workQueue 工作队列

    新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:

    ①ArrayBlockingQueue
    基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。

    ②LinkedBlockingQuene
    基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。

    ③SynchronousQuene
    一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。

    ④PriorityBlockingQueue
    具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

    threadFactory:线程工厂,是用来创建线程的。默认new Executors.DefaultThreadFactory();
    handler:线程拒绝策略。
    当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:

    ①CallerRunsPolicy
    该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
    ②AbortPolicy
    该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
    ③DiscardPolicy
    该策略下,直接丢弃任务,什么都不做。
    ④DiscardOldestPolicy
    该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
    到此,构造线程池时的七个参数,就全部介绍完毕了。

    Threadlocal
    ThreadLocal, 线程变量,为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
    使用场景:
    1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
    2、线程间数据隔离
    3、进行事务操作,用于存储线程事务信息。
    4、数据库连接,Session会话管理。
    包含方法:
    get()
    set()
    remove()
    initialValue()

    面试问题:
    作者:IamHYN
    链接:https://www.nowcoder.com/discuss/481903?source_id=profile_create&channel=1013
    来源:牛客网

    1. 线程池有哪些参数,执行流程是怎样的?有哪些常用 BlockingQueue,区别是什么?拒绝策略有哪些?shutdown() 和 shutdownNow() 有什么区别?

    2. synchronized 和 ReentrantLock 区别?ReentrantLock 实现原理,AQS 原理,CountdownLatch 和 Semophore 的作用?

    3. ThreadLocal 原理,线程池中使用 ThreadLocal 会有什么问题,为什么?软引用和弱引用有什么区别?

    展开全文
  • java线程池的使用

    2015-01-21 09:55:12
    1、在任务执行策略之间的隐性偶尔 虽然Executor框架为制定和修改执行策略都提供了相当大的灵活性,但并非所有的任务都能适用所有的执行策略。有些类型的任务需要明确地指定执行策略,包括:1:依赖性任务;2:适用...

    1、在任务与执行策略之间的隐性耦合

    虽然Executor框架为制定和修改执行策略都提供了相当大的灵活性,但并非所有的任务都能适用所有的执行策略。有些类型的任务需要明确地指定执行策略,包括:1:依赖性任务;2:适用线程封闭机制的任务;3.对响应时间敏感的任务;4:适用ThreadLocal的任务;

    (1)每次提交了一个有依赖性的Executor任务时,要清楚地知道可能会出现线程”饥饿:死锁,因此需要在代码或配置Executor的配置文件中记录线程池的大小限制与配置限制。

    (2)运行时间较长的任务

    限定任务等待资源的时间,可以缓解执行时间较长任务造成的影响。

    在平台类库的大多数可阻塞方法中,都同时定义了显示版本和无限时版本。

    例如:Thread.join、BlockingQueue.put、CountDownLatch.await以及Selector.select等


    2、设置线程池的大小

    线程池的理想大小取决于被提交任务的类型以及所部属系统的特性。在代码中,通常不会固定线程池的大小,而应该通过某种配置机制来提供,或者根据Runtime.availableProcessors来动态计算。

    计算密集型的任务,在拥有N(cpu)个处理器的系统上,当线程池的大小为n+1时,通常能实现最优的利用率;

    I/O密集型,由于线程并不会一直在执行,估算任务等待的时间与计算时间的比值;

    U(cpu)=cpu的利用率,0=<U<=1; w/c = 计算机计算等待的时间/计算时间

    线程池的最优大小:N(thread) = N(cpu)*U(cpu的效益)*(1+w/c)

    注:影响线程池大小资源,还包括内存、文件句柄、套结子句柄和数据库连接等;


    3、配置ThreadPoolExecutor

    (1)线程的创建与销毁

    线程池的基本大小、最大大小以及存活时间等因素共同负责线程的创建与销毁。

    (2)管理队列任务

    基本任务队列有3种:无界队列、有界队列和同步移交

    无界队列:当任务超过了线程池处理它们的速度,那么队列将无线增加;

    有界队列:就是队列的容量有限;

    同步移交:避免任务排队,直接将任务从生产者移交给消费者;

    (3)饱和策略

    当有界队列被填满后,饱和策略开始发挥作用。ThreadPoolExecutor可以通过调用setrejectedExecutionHandler来修改;

    RejectedExecutionHandler实现:AbortPolicy(默认),callerRunsPolicy,DiscardPolicy和DiscardOldestPolicy。

    (4)线程工厂

    每当线程池需要创建一个线程时,都是通过线程工厂方法来完成的。ThreadFactory接口中定义了一个方法:newThread();

    (5)在调用构造函数后再定制ThreadPoolExecutor

    在调用玩ThreadPoolExecutor的构造函数后,仍然可以通过设置函数(setter)来修改大多数传递给它的构造函数的参数;


    4、扩展ThreadPoolExecutor

    ThreadPoolExecutor是可扩展的,它提供了几个可以在子类化中改写的方法:beforeExecute、afterExecute和terminated,用于扩展ThreadPoolExecutor的行为。在这些方法中,可以添加日志、计时、监视或统计信息收集的功能。

         在线程池完成关闭操作时调用terminated,可以用来释放Executor在其生命周期里分配的各种资源。此外,还可以执行发送通知、记录日志或者收集finalize统计信息等操作。


    5、递归算法的并行化

    当串行循环中的各个迭代操作彼此独立时,并行每个迭代操作执行的工作亮比管理一个新任务带来的开销更多,那么这个串行循环就适合并行化。


    展开全文
  • 愿你越努力越幸运「歇一歇,继续奔跑」今天蓝猫讲解JavaThreadLocal的...常见面试点如何避免ThreadLocal导致内存泄露ThreadLocal的实现原理应用情景ThreadLocal结合线程池使用的问题什么是ThreadLocalJDK源码中...
  • 文章目录ThreadLocal简介方法原理分析线程池与ThreadLocal内存泄露 ThreadLocal 简介 ThreadLocal不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的...
  • 九:java中的线程池

    2016-07-25 22:16:58
    前记上一篇温习的是ThreadLocal共享...场景及代码线程池与Executors类的使用package com.test.thread;import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.conc
  • Java多线程高并发七本来想写ThreadLocal源码的,实在是看底层代码看不动了!先搁置吧。Java多线程高并发八给同学们分享下面试中常考的线程池的七大参数! 为什么要用线程池 防止频繁创建和销毁线程,让每个...
  • Java线程之线程池

    2018-12-28 22:28:31
     Executor框架可以将任务的提交任务的执行策略解耦开来。就像许多对复杂过程的解耦操作那样,这种论断多少有些言过其实了。虽然Executor框架为定制和修改执行策略提供了相当大的灵活性,但并非所有的任务都能适用...
  • 接上篇《Java并发技术总结之一——Java线程池》 二. ThreadLocal 参考地址:《Java中的ThreadLocal详解》 ThreadLocal 用来保证规避多线程访问线程不安全的情况,每个线程访问自己的副本变量,这样就避免了对资源...
  • 线程池——治理线程的最大法宝1、ThreadLocal1.1 两大使用场景——ThreadLocal的用途1.1 典型场景1:每个线程需要一个独享的对象1.2 典型场景2:每个线程内需要保存全局变量1.2 ThreadLocal方法1.3 ThreadLocal的两...
  • 线程池的执行策略(使用什么样的线程池的实现有关) 是否有返回值 工作线程的数量 是否设置超时时间 是否定时或者有计划的执行 线程池将任务的提交和任务的执行策略解耦和 但是对于某些种类的线程,线程池自带的...
  • 添加元素执行结束后要remove,否则如果与线程池配合使用,会导致元素一直存在线程中被占用,如果没有再次被使用既会变成内存泄漏,如果再次使用可能会有脏数据的问题。(为什么强调线程池,因为在线程池中线程才不会...
  • 文章目录线程池-前言线程池`ForkJoinPool`拆分任务线程相关类`ThreadLocal`类包装线程不安全的集合线程安全的集合 线程池-前言 系统启动一个线程的成本较高,而使用线程池可以提高性能,尤其在菜鸡大量短期线程时。...
  • 在任务执行策略之间的隐形耦合 依赖任务 使用线程封闭机制的任务 对相应时间敏感的任务 使用ThreadLocal的任务 在线程池中,如果任务依赖于其他任务,那么可能产生死锁。 程序清单8-1 在单线程...
  • 线程进程相似,但是是比进程更小的执行单位,多个线程共享进程的堆和方法区,但是每个线程有自己的程序计数器,虚拟机栈和本地方法栈 java的内存区域: 1、程序计数器:可以认为是当前线程所执行的字节码的字号...
  • 在任务执行策略之间的隐性耦合 Executor框架可以将任务的提交任务的执行策略解耦开来 将像许多对复杂过程的解耦操作那样 这种论断多少有些言过其实了 虽然Executor框架为制定和修改执行...使用ThreadLocal的任...
  • 1,Executor框架可以将任务的提交任务的执行策略解耦开来,并且为制定和修改执行策略都提供了相当大的灵活性,但并非所有的任务都能够使用所有的执行策略。有些类型任务需要明确地指定执行策略:依赖性任务,使用...
  • 线程池方式 优缺点 1.继承Thread类 优点 、代码简单 。 缺点 、该类无法集成别的类。 2.实现Runnable接口 优点 、继承其他类。 同一实现该接口的实例可以共享资源。 缺点 、代码复杂 3.实现Callable 优点
  • Java多线程编程-(3)-线程本地ThreadLocal的介绍使用 Java多线程编程-(4)-线程间通信机制的介绍使用 Java多线程编程-(5)-使用Lock对象实现同步以及线程间通信 Java多线程编程-(6)-两种...
  • 7.1 任务执行策略之间的隐性耦合Executor框架可以将任务的提交任务的执行策略解耦开,但这种说法有点言过其实。虽然Executor为制定和执行策略都提供了相当大的灵活性,但并非所有的任务都能适用所有的执行策略。...
  • 1、首先看一下代码,模拟了一个线程数为500的线程池,所有线程共享一个ThreadLocal变量,每一个线程执行的时候插入一个大的List集合: import java.util.ArrayList; import java.util.List; im...
  • 8.1 在任务执行策略之间的隐性耦合 有些类型的任务需要明确地指定执行策略,包括: ...线程池的线程中不应该使用ThreadLocal在任务之间传递值。只有当线程本地值的生命周期受限于任务的生命周...
  • 8.1 在任务执行策略之间的隐性耦合 虽然Executor框架为制定和修改执行策略都提供了相当大的灵活性,但并非所有的任务都适用所有的执行策略。有些类型的任务需要明确地指明执行策略: 依赖性任务 使用线程封闭...
  • 一、在任务运行策略之间的隐性解耦 有些类型的任务须要明白地指定运行策略,包含: . 依赖性任务。依赖关系对运行策略造成约束。须要注意活跃性问题。要求线程池足够大,确保任务都能放入。 . 使用线程封闭...
  • 8.1 在任务执行策略之间的隐形耦合 有些类型的任务需要明确指定执行策略,包括: 依赖性任务、使用线程封闭机制的任务、对响应时间敏感的任务、使用ThreadLocal的任务。 只有当任务都是同类型的并且相互独立时...
  • 前几篇: Java多线程编程-(1)-线程安全和锁Synchronized概念 Java多线程编程-(2)-可重入锁...Java多线程编程-(4)-线程本地ThreadLocal的介绍使用 Java多线程编程-(5)-线程间通信机制的介绍使用

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 142
精华内容 56
关键字:

java线程池与threadlocal

java 订阅