zero's Blog

持续迭代

1、先复现这种情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.sdcuike.java11;

import java.util.concurrent.*;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

public class Demo {

public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("rpc-pool-%d").setDaemon(true).build();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 1, TimeUnit.HOURS, new SynchronousQueue<>(), threadFactory, new ThreadPoolExecutor.DiscardPolicy() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("rejectedExecution");

super.rejectedExecution(r, executor);
}
});

threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
try {
TimeUnit.HOURS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

Future<String> future = threadPoolExecutor.submit(new Callable<String>() {

@Override
public String call() throws Exception {
return "done";
}
});

String result = future.get();
System.out.println(result);
System.out.println("done....");
}

}
Read more »

[TOC]

1.为什么需要使用线程池

  合理利用线程池能够带来三个好处。
  第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  第二:提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即行。
  第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

Read more »

一、线程六种状态

线程的状态在java中有明确的定义,在java.lang.Thread.State中有6种。

  • ① New:线程被创建,未执行和运行的时候
  • ② Runnable:不代表线程在跑,两种:被cpu执行的线程,随时可以被cpu执行的状态。
  • ③ Blocked:线程阻塞,处于synchronized同步代码块或方法中被阻塞。
  • ④ Waiting:等待先生的线程状态。线程当前不执行,如果被其他唤醒后会继续执行的状态。依赖另一个线程的通知的。这个等待是一直等,没人叫你,你起不来。
  • ⑤ Time Waiting:指定等待时间的等待线程的线程状态。带超时的方式:Thread.sleep,Object.wait,Thread.join,LockSupport.parkNanos,LockSupport.parkUntil
  • ⑥ Terminated:正常执行完毕或者出现异常终止的线程状态。
Read more »

首先抛几个问题:

  • Thread.interrupt()方法和InterruptedException异常的关系?是由interrupt触发产生了InterruptedException异常?
  • Thread.interrupt()会中断线程什么状态的工作? RUNNING or BLOCKING?
  • LockSupport.park()和unpark(),与object.wait()和notify()的区别?
  • LockSupport.park(Object blocker)传递的blocker对象做什么用?
  • LockSupport能响应Thread.interrupt()事件不?会抛出InterruptedException异常?
Read more »

JDK中的ThreadExecutor中的execute方法的处理逻辑应该都知道:

1.小于等于Coresize:创建线程执行;
2.大于CoreSize:加入队列;
3.队列满且小于maxSize:有空闲线程使用空闲线程执行,没有的话,创建线程执行;如果大于maxSize则拒绝策略执行。

这样会造成一个现象,如果设置的不恰当,队列使用LinkedBlockingQueue,那么线程数量是很不可能达到maximumPoolSize,因为线程数量到了corePoolSize之后,之后新的任务是添加到队列里面去了。

Tomcat中有一个StandardThreadExecutor线程池,该线程池execute执行策略是优先扩充线程到maximumPoolSize,再offer到queue,如果满了就reject。来看看StandardThreadExecutor是怎么实现的。

Read more »

线程池使用FutureTask的时候如果拒绝策略设置为了DiscardPolicy和DiscardOldestPolicy并且在被拒绝的任务的Future对象上调用无参get方法那么调用线程会一直被阻塞。

问题复现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public static void main(String[] args) throws Exception {

//(1)线程池单个线程,线程池队列元素个数为1
ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1,
1L, TimeUnit.MINUTES,
new ArrayBlockingQueue<>(1),
new ThreadPoolExecutor.DiscardPolicy());

//(2)添加任务one
Future futureOne = executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("start runable one");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

//(3)添加任务two
Future futureTwo = executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("start runable two");
}
});

//(4)添加任务three
Future futureThree = null;
try {
futureThree = executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("start runable three");
}
});
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getLocalizedMessage());
}

System.out.println("task one finish " + futureOne.get());//(5)等待任务one执行完毕
System.out.println("task two finish " + futureTwo.get());//(6)等待任务two执行完毕
System.out.println("task three finish " + (futureThree == null ? null : futureThree.get()));// (7)等待任务three执行完毕

executorService.shutdown();//(8)关闭线程池,阻塞直到所有任务执行完毕
}

Read more »

关于线程池的介绍在http://blog.csdn.net/zero__007/article/details/43795287http://blog.csdn.net/zero__007/article/details/44102239 已经介绍了,这里只是稍微补充补充。

RejectedExecutionHandler

ThreadPoolExecutor的构造函数中会有该参数,表示拒绝策略。当队列满且线程池大小>=maximumPoolSize时会触发驳回,因为这时线程池已经不能响应新提交的任务,驳回时就会回调这个接口rejectedExecution方法,JDK默认提供了4种驳回策略,可以根据业务场景来选择,线程池的默认策略是AbortPolicy。

  • ThreadPoolExecutor.AbortPolicy:直接抛出运行时异常。
  • ThreadPoolExecutor.CallerRunsPolicy: 转成同步调用。
  • ThreadPoolExecutor.DiscardPolicy: 直接丢弃。
  • ThreadPoolExecutor.DiscardOldestPolicy: 取出队列的头并丢弃,重新提交该任务。

ThreadPoolExecutor预留了以下三个方法,我们可以通过继承该类来做一些扩展,比如监控、日志等等。

1
2
3
protected void beforeExecute(Thread t,  Runnable r) { }  
protected void afterExecute(Thread t, Runnable r) { }
protected void terminated() { }
Read more »

先上两段代码:

1
2
3
4
5
6
7
8
9
ExecutorService threadPool = Executors.newFixedThreadPool(1);
threadPool.execute(() -> {
System.out.println("execute");
Object obj = null;
System.out.println(obj.toString());
});
threadPool.shutdown();
System.out.println("**********");

1
2
3
4
5
6
7
8
9
ExecutorService threadPool = Executors.newFixedThreadPool(1);
threadPool.submit(() -> {
System.out.println("submit");
Object obj = null;
System.out.println(obj.toString());
});
threadPool.shutdown();
System.out.println("**********");

运行结果发现,execute执行的时候抛出了预期的NullPointerException异常,而submit执行时什么都没有!

Read more »

当单线程应用程序中的主线程抛出一个未捕获的异常时,因为控制台中会打印堆栈跟踪(也因为程序停止),所以很可能注意到。但在多线程应用程序中,尤其是在作为服务器运行并且不与控制台相连的应用程序中,线程死亡可能成为不太引人注目的事件,这会导致局部系统失败,从而产生混乱的应用程序行为。

编写得不正确的线程池会“泄漏”线程,直到最终丢失所有线程。大多数线程池实现通过捕获抛出的异常或重新启动死亡的线程来防止这一点,但线程泄漏的问题并不仅限于线程池,使用线程来为工作队列提供服务的服务器应用程序也可能具有这种问题。当服务器应用程序丢失了一个工作线程(worker thread)时,在较长时间内应用程序仍可能显得一切正常,这使得该问题的真实原因难以确定。

许多应用程序用线程来提供后台服务:处理来自事件队列的任务、从套接字读取命令或执行UI线程以外的长期任务。当由于抛出未捕获的 RuntimeException 或 Error ,或者只是停下来,等待阻塞的 I/O 操作(原本未预计到阻塞),从而引起这些线程之一死亡时,会发生什么呢?

有时,譬如当线程执行由用户启动的长期任务(如拼写检查)时,用户会注意到任务没有进展,他们可能会异常终止操作或程序。但其它时间,后台线程执行“清理维护”任务 ,它们可能消失很长时间而不被察觉。

Read more »

​ ThreadLocal,线程本地存储,为变量在每个线程中都创建了一个副本,那么每个线程可以独立地改变和访问自己的副本变量,而不会影响其它线程所对应的副本变量。从线程的角度看,目标变量就像是线程的本地变量,这也是类名中“Local”所要表达的意思。

​ ThreadLocal不是用来解决对象共享访问问题的,而是提供了保持对象的方法和避免参数传递的对象访问方式。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象。

​ 在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个 threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本。初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对 Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为 value,存到threadLocals。然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

Read more »
0%