线程(池)总结
线程
1.什么是线程?
进程:是程序的一次执行过程,是操作系统资源分配的基本单位
线程:是计算机中独立运行的最小单位,运行时占用很少的系统资源。
由于每个线程占用的CPU时间是系统分配的,因此可以把线程看成操作系统分配CPU时间的基本单位。
线程基本上不拥有系统资源,它与同属于一个进程的线程共享进程拥有的全部资源。
2.线程的创建方式
①继承Thread类重写run方法②参数传递Runnable对象③参数传递Callable对象,返回FutureTask
3.线程常用方法
3.1 sleep:使当前线程睡眠一段时间
//1.,当前线程不会释放任何监视器的所有权(不释放对象锁)
//2.被打断后,在抛出异常时,打断状态会被clear,也就是调用isInterrupted会返回fasle
public static native void sleep(long millis) throws InterruptedException;
3.2 join:等待某个线程运行结束
//(比如main线程中 调用t1线程的start(),join()后,则main线程需要等待t1线程运行完之后继续执行,main线程进入 WAITING态)
public final void join() throws InterruptedException {...}
- 可以实现同步
3.3 yield :让出线程执行权
//1.调用 yield 会让调用线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
//2.很少用,可能对调试和测试的目的有用
public static native void yield();
3.4 interrupt: 打断某个(阻塞)线程
//1.打断由于调用了wait,join,sleep而陷入阻塞的线程时,会抛出InterruptedException,并且清除打断状态
//2.如果一个线程阻塞在一个IO操作(InterruptibleChannel),调用此方法会关闭channel,并设置线程的打断状态,并且此线程会收到1个ClosedByInterruptException
public void interrupt() {...}
3.5 setPriority:
线程的切换是由线程调度控制的,我们无法通过代码来干涉,但是我们可以通过提高线程的优先级来最大程度的改善线程优先获取时间片的几率
Java中线程的优先级被划分为10级,值分别为1-10,1最低,10最高。Thread类提供了3个常量来表示最低最高和默认优先级.
Thread.MIN_PRIORITY = 1;
Thread.NORM_PRIORITY = 5;
Thread.MAX_PRIORITY = 10;
public final void setPriority(int newPriority) {...}
3.6 setDaemon
在Java中有两类线程:User Thread(用户线程)、Daemon Thread在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。
public final void setDaemon(boolean on) {...}
4. 线程的状态
4.1 NEW
: new
出来后 start
()前
4.2 RUNNABLE
: 在Java虚拟机中执行但可能在等操作系统的某个资源,比如CPU
4.3 BLOCKED
: 等一个monitor锁时以便进入同步块/方法中
4.4 WAITING
: 调用了Object
#wait
(),Thread
#join
(),LockSupport
#park
()后, 在等待其他线程执行一个特殊的操作
4.5 TIMED_WAITTING
: 调用了这些Thread.sleep
,Object
#wait(long),join
(long),LockSupport
#parkNanos
,LockSupport
#parkUntil
方法后。
4.6 TERMINATED
:run方法执行完后。
5. 线程安全问题的解决synchronized
5.1同步与互斥
- 互斥是保证临界区的竞态条件发生,同一时刻只能有一个线程执行临界区代码
- 同步是由于线程执行的先后、顺序不同、需要一个线程等待其它线程运行到某个点
5.2 synchronized
用法,代码块和方法上,为什么需要类锁?
- 锁静态方法,有些东西是属于一个类的。
5.3 JDK1.6的优化 偏向锁和轻量级锁
线程池
1.什么是线程池,为什么要有线程池?
就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销.
JDK中用HashSet
2.核心线程是如何保持不被销毁的?
去任务时阻塞,BlockQueue
#getTask
(),如果没有任务会调用条件变量的await方法等待,直到有为止。
非核心线程不是阻塞获取,是超时获取的null,也就会任务结束,线程终止。
核心方法getTask(),参加分析https://www.cnblogs.com/DDiamondd/p/11362164.html
3.Executors返回的4种线程池
3.1 newFixedThreadPool
public static ExecutorService newFixedThreadPool(public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
}
特点
- 核心线程数 == 最大线程数(没有救急线程被创建),因此也无需超时时间
- 阻塞队列是无界的,可以放任意数量的任务
适用于任务量已知,相对耗时的任务
3.2 newCachedThreadPool
public static ExecutorService newCachedThreadPoolpublic static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new
SynchronousQueue());
}
核心线程数是 0, 最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是 60s,意味着
全部都是救急线程(60s 后可以回收),救急线程可以无限创建
队列采用了 SynchronousQueue 实现特点是,它没有容量,没有线程来取是放不进去的(一手交钱、一手交 货)
整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲 1分钟后释放线 程。 适合任务数比较密集,但每个任务执行时间较短的情况
3.3 newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return public static ExecutorService newSingleThreadExecutor() { return new
FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }
使用场景: 希望多个任务排队执行。线程数固定为 1,任务数多于 1 时,会放入无界队列排队。任务执行完毕,这唯一的线程 也不会被释放。 区别:
自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建一 个线程,保证池的正常工作
Executors.newSingleThreadExecutor
() 线程个数始终为1,不能修改FinalizableDelegatedExecutorService
应用的是装饰器模式,只对外暴露了ExecutorService
接口,因此不能调用ThreadPoolExecutor
中特有的方法Executors.newFixedThreadPool
(1) 初始时为1,以后还可以修改对外暴露的是ThreadPoolExecutor
对象,可以强转后调用setCorePoolSize
等方法进行修改
3.4 任务调度线程池
在『任务调度线程池』功能加入之前,可以使用 java.util.Timer 来实现定时功能,Timer 的优点在于简单易用,但 由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个 任务的延迟或异常都将会影响到之后的任务。
ScheduledExecutorService executor = Executors.newScheduledThreadPool(ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
- 可以延时执行任务
executor.schedule
() - 也可以定时执行任务
scheduleAtFixedRate
scheduleWithFixedDelay
4. 线程池异常处理
- 任务主动自己try catch
- 使用Future,Callable处理
5. 一些概念的区分
5.1 核心线程 和非核心线程?
>=ThreadPoolExecutor
#corePoolSize
后的线程是非核心线程(也叫救急线程)
5.2 线程池当前pool size(池子的大小)?
private final HashSet<Worker> workers = private final HashSet<Worker> workers = new HashSet();
// workers.size()表示
5.3 active thread 数量(活动线程数量)
为正在执行任务的线程,通过遍历workers , worker.isLocked()返回true的是active thread
6.线程池机制的总结
6.1
6.2 线程池不终止,核心线程启动后就不会主动终止,也就是说当poolSize
超过corePoolSize
时,线程池中没有任何任务时,pool
size 为corePoolSize
的大小 ,通过ThreadPoolExecutor
#toString
方法可以看
public String toStringpublic String toString() {
long ncompleted;
int nworkers, nactive;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
ncompleted = completedTaskCount;
nactive = 0;
nworkers = workers.size();
for (Worker w : workers) {
ncompleted += w.completedTasks;
if (w.isLocked())
++nactive;
}
} finally {
mainLock.unlock();
}
int c = ctl.get();
String rs = (runStateLessThan(c, SHUTDOWN) ? "Running" :
(runStateAtLeast(c, TERMINATED) ? "Terminated" :
"Shutting down"));
return super.toString() +
"[" + rs +
", pool size = " + nworkers +
", active threads = " + nactive +
", queued tasks = " + workQueue.size() +
", completed tasks = " + ncompleted +
"]";
}
pool size : 线程池中线程总数(不是线程池的最大线程数) ,开始从0 涨到核心池的数量,阻塞队列满后,再增加到max pool size
active threads:活动线程(有任务在执行) 的数量
queued tasks: 阻塞队列中的任务数、
completed tasks : 已经执行完的任务数
7.阿里巴巴Android规范对于线程池的要求
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方 式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
Executors
返回的线程池对象的弊端如下:
1)FixedThreadPool
和SingleThreadPool
:允 许 的 请 求 队 列 长 度 为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致;OOM
2)CachedThreadPool
和ScheduledThreadPool
:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
正例
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue taskQueue = new LinkedBlockingQueue();
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES,
NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT,
taskQueue, new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());
反例
ExecutorService cachedThreadPool = Executors.newExecutorService cachedThreadPool = Executors.newCachedThreadPool();
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!