Java线程相关知识,线程池底层源码分享和相关面试题(持续更新)
线程
线程有哪些状态
*/
NEW, //当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread()
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE, //当调用线程对象的start()方法,线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了start()此线程立即就会执行
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED, //处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING, //这个状态下是不能分配CPU执行的,可使用Object.wait(),Thread,join(),LockSupport.park进入这个状态
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,//其实这个状态和Waiting就是有没有超时时间的差别,这个状态下也是不能分配CPU执行的
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED; //在我们的线程正常run结束之后或者run一半异常了就是终止状态
终止线程的方式
1.Tread.stop(). 2.Tread.interrupt()
推荐使用interrupt,因为使用stop之后,这个线程带有的锁也会消失,因此不推荐使用这个方法,interrupt()会使得线程Waiting和Timed_Waiting状态的线程抛出 interruptedException异常,调用interrupt()方法后,使得Running状态的线程再调用wait()、sleep()、jion()方法时抛出interruptedException异常,需要在catch中处理线程异常后的问题。
进程和线程的区别
来源于一个知乎老哥 https://www.zhihu.com/question/25532384 比较容易理解
做个简单的比喻:进程=火车,线程=车厢
线程在进程下行进(单纯的车厢无法运行)
一个进程可以包含多个线程(一辆火车可以有多个车厢)
不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁"
进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”
sleep方法和wait方法区别
都可以使当前线程放弃CPU一定的时间从而暂停,但是如果有锁的话,sleep方法不会释放这个锁,wait方法会, sleep方法必须要设定时间,wait方法可设定可不设定。 sleep属于Thread类,wait方法属于Object类
wait,notify方法存在的问题(Condition 下的await和signal也是一样)
notify和wait必须在synchrnizied同步块中执行,并且成对出现,只有先wait再notify才能被唤醒,否则无法唤醒。
1.继承Thread类,重写run方法
2.实现Runnable接口,重写run方法
3.实现Callable接口,重写call方法
4.使用线程池
ThreadLocal原理
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

进程之间的通信方式有哪些
管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
线程池
线程池各个参数讲解
public ThreadPoolExecutor(int corePoolSize, //线程池核心工作线程数量,比如newFixedThreadPool中可以自定义的线程数量就是这个参数
int maximumPoolSize, //线程池所有工作线程的数量,比如newFixedThreadPool中的最大工作线程就是核心线程数,newCachedThreadPool中的最大工作线程数是Integer.MAX_VALUE
long keepAliveTime, //非核心线程存活的时间,当核心线程不够用的时候,创建出来的辅助工作线程在完成任务空闲多久后会被回收
TimeUnit unit, //上面一个参数的单位,分。秒等
BlockingQueue<Runnable> workQueue,//底层使用的阻塞队列数据结构,比如newFixedThreadPool底层使用LinkedBlockingQueue。工作队列,保存已提交但是未执行的任务
ThreadFactory threadFactory, //创建工作线程的工厂,保持默认即可
RejectedExecutionHandler handler) { //拒绝策略,即在所有工作线程数达到上限,底层阻塞队列也满了的时候采取的策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
常用线程池
日常使用中使用哪种线程池
上面四种都不使用,我们需要通过ThreadPoolExcutor自定义线程池,阿里巴巴java开发手册明确指出,不允许使用Excutors工具类进行线程池的创建,因为某些开发人员不了解底层运行规则,避免资源耗尽的危险
1:FixedThreadPool 和 SingleThreadPool: 允许的请求队列(底层实现是LinkedBlockingQueue)长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
2:CachedThreadPool 和 ScheduledThreadPool 允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM
线程池4种拒绝策略
new ThreadPoolExecutor.AbortPolicy(); //遇到线程池达到最大线程数,且队列已经满了,直接抛异常 ----默认
new ThreadPoolExecutor.DiscardOldestPolicy() //遇到线程池达到最大线程数且队列已经满了,则丢弃队列中最前面的任务,
new ThreadPoolExecutor.DiscardPolicy() //遇到线程池达到最大线程数且队列已经满了,丢弃当前任务,不给它加入队列中
new ThreadPoolExecutor.CallerRunsPolicy();//会将任务返回给提交者进行执行,比如让main线程直接执行run方法
线程池优点
1.避免大量线程的创建销毁带来的性能开销
2.避免大量线程之间因为相互抢占系统资源导致的阻塞状态
3.能够对线程进行简单的管理并提供定时执行,间隔执行等功能
在IO密集型和CPU密集型下线程池最大线程数选择
在CPU密集型下,选用cpu核心数+1作为最大线程池工作线程数量,减少cpu切换任务带来的开销
在IO密集型下,尽可能多的加大工作线程数量,一般为cpu核心数*2
线程池提交任务时excute和submit有什么区别
1.两者接收参数不一致,excute接收Runnable接口,submit接收Callable接口
2.submit有返回值,execute没有返回值
3.submit支持返回Future
线程池状态
private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static final int STOP = 1 << COUNT_BITS; private static final int TIDYING = 2 << COUNT_BITS; private static final int TERMINATED = 3 << COUNT_BITS;
RUNNING:线程池的初始状态,可以添加待执行的任务
SHUTDOWN:线程池处于待关闭状态,不接受新任务,但会处理完已接收的任务
STOP:线程池立即关闭,不接收新的任务,放弃缓存队列中的任务并且中断正在处理的任务
TIDYING:线程池自主整理状态,调用 terminated() 方法进行线程池整理
TERMINATED:线程池终止状态


浙公网安备 33010602011771号