进程与线程的的区别
线程的生命周期
线程池的由来
线程池的7大参数
线程池的工作流程
线程池的四大拒绝策略:
常用的线程池有哪些
一、线程和进程的区别
线程,程序执行流的最小执行单位,是行程中的实际运作单位,
一个应用程序的运行就可以被看做是一个进程,而线程就是应用程序中的一个任务。
线程的三种创建方法
-
extends Thread类
-
实现Runnable方法
-
实现callable方法 有返回值
二、线程的生命周期
线程的生命周期,线程的生命周期可以利用以下的图解来更好的理解:
Run方法和start方法的区别
start方法会启动一个分支线程,为其分配新的内存空间,start方法瞬间结束。
run方法不会启动线程,不会分配新的分支栈 只是一个普通的方法。
package com.lj.thread;
//实现线程的第一种方法
public class ThreadTest01 {
public static void main(String[] args) {
MyThread mt = new MyThread(); //3
//mt.run();这条语句执行完才会执行下面的
mt.start(); //4
for(int i = 0;i<100;i++){
System.out.println("主线程---->"+i);
}
}
}
class MyThread extends Thread { //1
@Override
public void run() { //2
for(int i = 0;i<100;i++){
System.out.println("分支线程----->"+i);
}
}
}
为什么会造成多线程的安全问题呢?
在并发环境下,多个线程对同一共享资源进行操作,有可能发生线程安全问题。
解决线程安全问题的方法?
使用锁 同步代码块 尽量使用局部变量
并行和并发:
并发,从宏观方面来说,并发就是同时进行多种时间,实际上,这几种时间,并不是同时进行的,而是交替进行的,而由于CPU的运算速度非常的快,
并行,则是真正意义上的同时进行多种事情。这种只可以在多核CPU的基础下完成。
四,线程池
线程的创建并销毁线程的过程势必会消耗内存。
线程池的好处: 管理线程 减少内存的消耗。
Java中提供了创建线程池的一个类:Executor
而我们创建时,一般使用它的子类:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
1、corePoolSize(核心线程大小):当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时,( 也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)
2、maximumPoolSize(线程池最大大小):线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数。
3、keepAliveTime(线程存活保持时间)当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。
4、workQueue(任务队列):用于传输和保存等待执行任务的阻塞队列。
5、threadFactory(线程工厂):用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。
6、handler(线程饱和策略):当线程池和队列都满了,再加入线程会执行此策略。
线程池的执行流程
当有任务进来时1、判断核心线程池是否已满,没满则创建一个新的工作线程来执行任务。已满则。 2、判断任务队列是否已满,没满则将新提交的任务添加在工作队列,已满则。 3、判断整个线程池是否已满,没满则创建一个新的工作线程来执行任务,已满则执行拒绝策略。
拒绝策略:
AbortPolicy(中止策略) 默认 抛异常 DiscardPolicy 不做任何处理策略 DiscardOldestPolicy(抛弃最老策略) CallerRunsPolicy 交由调用者调用策略
五、java中提供的线程池
Executors类提供了4种不同的线程池:newCachedThreadPool, newFixedThreadPool, newScheduledThreadPool, newSingleThreadExecutor
ExecutorService threadPool = Executors.newFixedThreadPool(5);
threadPool.execute();//开启线程池
threadPool.shutdown();//关闭线程池
java线程池对比
1、newCachedThreadPool:用来创建一个可以无限扩大的线程池,可能会创建大量的线程,从而导致OOM
2、newFixedThreadPool:创建一个固定大小的线程池,可能会堆积大量的请求,从而导致OOM
3、newSingleThreadExecutor:创建一个单线程的线程池可能会堆积大量的请求,从而导致OOM
4、newScheduledThreadPool:适用于执行延时或者周期性任务。可能会创建大量的线程,从而导致OOM
【强制】线程池不允许使用Executors 去创建,而是通过ThreadPoolExecutor的方式
上面四种线程池的创建底层都是new一个ThreadPoolExecutor();
建议自定义线程池
六、execute()和submit()方法
1、execute(),执行一个任务,没有返回值。 2、submit(),提交一个线程任务,有返回值。 submit(Callable<T> task)能获取到它的返回值,通过future.get()获取(阻塞直到任务执行完)。一般使用FutureTask+Callable配合使用(IntentService中有体现)。
submit(Runnable task, T result)能通过传入的载体result间接获得线程的返回值。 submit(Runnable task)则是没有返回值的,就算获取它的返回值也是null。
Future.get方法会使取结果的线程进入阻塞状态,知道线程执行完成之后,唤醒取结果的线程,然后返回结果。