Java多线程理解
- 1.概念
- 2.多线程的作用
- 3. 创建线程的方式
- 4.start()方法和run()方法的区别
- 5.Runnable接口和Callable接口的区别
- 6.线程池
- 1)自定义线程池的创建
- 2)线程池参数解释:
- 3)四种常用的线程池
- 1. newCachedThreadPool
- 2.newFixedThreadPool
- 3.newSingleThreadExecutor
- 4.newScheduleThreadPool
- 2)工作过程
- 3)线程池工作队列
- 4)线程池拒绝策略
- 7.未完待续...
1.概念
2.多线程的作用
- 发挥多核CPU的优势。**单核CPU上所谓的"多线程"那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程"同时"运行罢了。**现在的计算机都是双核的,4核、8核甚至16核的,如果是单线程的程序,那么在双核CPU上就浪费了50%,在4核CPU上就浪费了75%。多核CPU上的多线程才是真正的多线程,它能让你的多段逻辑同时工作,多线程,可以真正发挥出多核CPU的优势来,达到充分利用CPU的目的。
- 防止阻塞。从程序运行效率的角度来看,单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运行多线程导致线程上下文的切换,而降低程序整体的效率。而单核CPU应用多线程,就是为了防止阻塞,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。
- 便于建模。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。
3. 创建线程的方式
4.start()方法和run()方法的区别
- 通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
- run: run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
- 总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。
5.Runnable接口和Callable接口的区别
- Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;
- Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
6.线程池
1)自定义线程池的创建
ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit,BlockingQueue<Runnable>,ThreadFactory,RejectedExecutionHandler)
2)线程池参数解释:
3)四种常用的线程池
1. newCachedThreadPool
解释: 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
特点:
- 配置corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE,keepAliveTime=60s,以及一个无容量的阻塞队列 SynchronousQueue,因此任务提交之后,将会创建新的线程执行;线程空闲超过60s将会销毁
2.newFixedThreadPool
解释: 创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
特点:
- 使用了一个无界LinkedBlockingQueue存放阻塞任务,因此多余的任务将存在再阻塞队列,不会由RejectedExecutionHandler处理
3.newSingleThreadExecutor
解释: 创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
特点:
- 无界阻塞队列LinkedBlockingQueue;保证任务由一个线程串行执行
4.newScheduleThreadPool
解释:创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
特点: 无界延迟阻塞队列DelayedWorkQueue。
2)工作过程
3)线程池工作队列
(1)ArrayBlockingQueue:规定大小的BlockingQueue,其构造必须指定大小。其所含的对象是FIFO顺序排序的。
(2)LinkedBlockingQueue:大小不固定的BlockingQueue,若其构造时指定大小,生成的BlockingQueue有大小限制,不指定大小,其大小有Integer.MAX_VALUE来决定。其所含的对象是FIFO顺序排序的。
(3)PriorityBlockingQueue:类似于LinkedBlockingQueue,但是其所含对象的排序不是FIFO,而是依据对象的自然顺序或者构造函数的Comparator决定。
(4)SynchronizedQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成。
4)线程池拒绝策略
- AbortPolicy
简单粗暴,直接抛出拒绝异常,这也是默认的拒绝策略。 - CallerRunsPolicy
如果线程池未关闭,则会在调用者线程中直接执行新任务,这会导致主线程提交线程性能变慢。 - DiscardPolicy
从方法看没做任务操作,即表示不处理新任务,即丢弃。 - DiscardOldestPolicy
抛弃最老的任务,就是从队列取出最老的任务然后放入新的任务进行执行。
7.未完待续…