首先提到了线程类,那么在java中建立(实现)线程是有三种方式的:html
实现interface Runnable的run() 继承class Thread override run() 使用FutureTask的方式(此方式有返回值,可是实际上仍是要依赖于Thread类)java
补充FutureTask方式:面试
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Main { public static void main(String[] args) { FutureTask<String> futureTask = new FutureTask<>(new CallerTask()); new Thread(futureTask).start(); try { String result = futureTask.get(); System.out.println(result); } catch (Exception e) { e.printStackTrace(); } } } class CallerTask implements Callable<String> { @Override public String call() throws Exception { return "i am a baby"; } }
综合上面三种方式,结合问题的构造方法,那么答案应该就是Thread的构造方法了。编程
提到静态代码块:先找到静态代码块的概念:安全
在Java中使用static和{}声明的代码块就是静态代码块。多线程
查了半天也没找到很明确的指向:看到群友说百度到了:静态代码块是被new这个线程类所在线程所调用的。并发
静态代码块是在类加载的时候调用的,且类加载器是缺省规则,那么调用静态代码块的线程就是调用当前类加载器的线程 参考文档dom
同步方法:在方法上加入关键字:synchronized的方法异步
同步代码块:在代码块上加入关键字:synchronized(Object){} (小括号内即为加锁的对象)ide
选用同步代码块好,由于能够只将可能出现并发问题的代码放入同步代码块中,且同步代码块能够自行选择加锁的对象。同步方法的锁对象是当前类的实例
死锁:两个或者两个以上的线程在执行过程当中,由于争夺资源而形成的互相等待的现象。(锁已经没有人拿了,可是如今全部的线程都在等待拿锁,而不是去拿锁)
死锁的四个必要条件:
互斥条件:资源只能被一个线程占用,若资源已经被占用,其余线程须要等待知道占用线程释放资源。
请求并持有条件:一个线程已经占用至少一个资源,又提出新的资源占用,可是新资源被其余线程占用,因此当前线程被阻塞,且不会释放已经占用的资源。
不可剥夺条件:线程占用资源后,在使用完以前是不会释放的,且不可能被其余线程夺走。
环路等待条件:发生死锁时,必然存在一个线程——资源的环形链。(请求并持有条件串成环既是)。
避免死锁须要破坏至少一个死锁的必要条件便可,实际上只有请求并持有和环路等待是能够破坏的。
那么如何破坏请求并持有条件和环路条件呢?实际上经过资源申请的有序性就能够实现。(反过来理解也能够认为是由于资源申请的有序性没法获得保障才致使的请求并持有和环路等待)
线程池的做用跟解决的问题实际上是一个概念:
解决两个问题:
一:大量异步任务时,线程池可以提供更好的性能:线程可重复利用,避免建立线程和销毁线程带来的资源损耗。
二:线程池提供了一种资源限制和管理的手段,能够限制线程的个数,也可动态新增线程池。
线程池以及场景和区别:
1.newFixedThreadPool:建立了一个核心线程个数和最大线程个数都为nThreads的线程池,且其阻塞队列长度为Integer.MAX_VAlUE。其keepAliveTime=0说明只要线程个数比核心线程个数多而且当前空闲就回收。注:关于这个keepAliveTime=0的回收概念,我我的暂时有点不理解(由于核心线程数和最大线程数是一个值)。
2.newSingleThreadExecutor:建立一个核心线程个数和最大线程个数都为1的线程池,且阻塞队列长度为Integer.Max_VALUE。keepAliveTime=0说明线程个数比核心线程个数多且当前空闲就回收。
3.newCachedThreadPool:建立一个按需建立线程的线程池,初始线程个数为0,最多线程个数为Integer.MAX_VALUE,而且阻塞队列为同步队列,keepAliveTime=60说明只要当前线程在60s内空闲则回收。这个类型特殊在加入同步队列的任务会被立刻执行,同步队列中最多只有一个任务。
4.ScheduledThreadPoolExecutor:主要是指定必定延迟时间后或者定时执行任务调度执行的线程池。
实现原理:当向线程池提交一个任务后,线程池进行处理:
一:线程池判断线程池的线程是否都在执行任务,若是不是,则建立一个新的工做线程来执行任务(使用threadFactory建立),若是线程都在执行任务,进入下个流程。
二:线程池判断工做队列是否满了,若是没有,则将新提交的任务存储在工做队列中(对应构造函数中的workQueue),若是满了进入下个流程。
三:线程池判断线程池是否已经满了(线程已经达到最大数,且任务队列已经满了),若是没有,则建立一个新的工做线程执行任务,若是满了,任务将被拒绝并交给饱和策略来处理这个任务。
应用场景其实就是问题一种的前三个线程池的应用场景:
一:FixedThreadPool:保证全部任务都执行,永远不会拒绝新任务;缺点是:队列数量没有限制,在任务执行无限延长的极端状况下可能形成内存问题。
二:SingleThreadExecutor:适用于逻辑上须要单线程的场景,同时无解的LinkedBlockingQueue保证新任务都可以放入队列,不会被拒绝,缺点也是可能会形成内存问题。
三:CachedThreadPool:提交的任务会当即分配线程执行,线程的数量会随着任务数进行扩展和缩减,问题是可能会建立过多的线程。
一共四中拒绝策略:
一:ThreadPoolExecutor.AbortPolicy 抛弃任务并抛出RejectedExecutionException
二:ThreadPoolExecutor.DiscardPolicy 抛弃任务,可是不抛出异常
三:ThreadPoolExecutor.DiscardOldestPlicy 抛弃队列最前面的任务,执行后面的任务
四:ThreadPoolExecutor.callerRunsPolicy 由调用线程处理该任务
是一个支持延期获取元素的无界限队列,队列使用PriorityQueue实现,队列中的元素必须实现Delayed接口,建立元素时能够指定多久才能从队列中获取当前元素,只有在延时期满时才能从队列中获取元素。
PriorityQueue是一个优先级的队列,队列中的元素会按照优先级进行排序。
CountDownLatch:能够实现计数器功能,使线程按照必定执行顺序执行。
CyclicBarrier:可让一组线程等待至某个状态以后再所有执行,当全部等待线程被释放后,CyclicBarrier可以重复使用
15日反思:关于并发方面差的东西有点多,最近正在补,目前看的书是《Java并发编程之美》,但愿能尽快吸取消化进步。
争取可以回头再把问题答案从新整理一遍
同步集合:
- Hashtable
- Vector
- 同步集合包装类:Collections下面的方法能够获取线程安装的同步集合:
synchronizedCollection(Collection<T> c)
synchronizedList(List<T> list)
synchronizedMap(Map<k, v> m)
synchronizedSet(Set<T> set)
synchronizedSortedMap(SortedMap<k ,v> sm)
同步集合的优缺点:在单线程环境下能够保证数据安全,可是是经过synchronized关键字实现同步方法将访问操做串行化,致使在并发环境中效率下降。且在多线程环境下的复合操做是非线程安全的,须要加锁来实现。
并发集合:
Java提供了两类的并发集合:
- 阻塞式集合(blocked collection):这类集合包含添加和移除的方法,当集合已满或者为空时被调用的添加或者移除方法就不能马上执行,那么调用这个方法的线程将会被阻塞,一直到改方法被调用成功。
- 非阻塞式集合(Non-blocked collection): 这类集合包含添加和移除方法,若是集合已满或者为空时,调用添加或移除方法会返回null或者报错,可是调用这个方法的线程不会被阻塞。
常见的并发集合包括:
- 非阻塞列表对应的实现类:ConcurrentLinkedDeque
- 阻塞式列表对于的实现类:LinkedBlockingDeque
- 用于数据生成或者消费的阻塞式列表对应的实现类:LinkedTransferQueue
- 按照优先级排序列表元素的阻塞式列表对应的实现类:PriorityBlockingQueue
- 带有延迟列表元素的阻塞式列表对应的实现类:DelayQueue
- 非阻塞式列表可遍历映射对应的实现类ConcurrentSkipListMap
- 随机数字对应的实现类:ThreadLockRandom
- 原子变量对应的实现类:AtomicLong和AtomicIntegerArray
事件派发线程是swing的组件,swing是事件驱动的,全部的操做都须要事件派发线程去完成,可是有些很费事的操做不太好放到事件派发线程中去。
SwingUtils提供了两个方法:invokeAndWait和invokeLater,他们都是事件派发线程可运行的对象,当对象位于事件派发队列的队首时,他们就被执行其中的run(),方法是容许事件派发线程调用另外一个线程中的任意一个方法。
invokeAndWait和invokeLater均可将可运行对象放到事件派发队列中去,可是invokeLater将对象放入队列就返回了,invokeAndWait将对象放入后直到已启动了可运行的run()时才返回。
Runnable是一个接口,封装了一个没有参数和返回值异步执行的方法。
Future也是一个接口,且其中保存异步计算的结果:
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
FutureTask则同时实现了上述两个接口
FutureTask内部提供了定义了如下变量:
volatile int state:表示对象状态,volatile关键字保证了内存可见性。futureTask中定义了7种状态,表明了7种不一样的执行状态
private static final int NEW = 0; //任务新建和执行中 private static final int COMPLETING = 1; //任务将要执行完毕 private static final int NORMAL = 2; //任务正常执行结束 private static final int EXCEPTIONAL = 3; //任务异常 private static final int CANCELLED = 4; //任务取消 private static final int INTERRUPTING = 5; //任务线程即将被中断 private static final int INTERRUPTED = 6; //任务线程已中断
Callable callable:被提交的任务
Object outcome:任务执行结果或者任务异常
volatile Thread runner:执行任务的线程
volatile WaitNode waiters:等待节点,关联等待线程
long stateOffset:state字段的内存偏移量
long runnerOffset:runner字段的内存偏移量
long waitersOffset:waiters字段的内存偏移量
注:后三个字段是配合Unsafe类作CAS操做使用的。
建立一个futureTask对象task 提交task到调度器executor等待调度或者在另一个线程中执行task 等待调度中... 若是此时currentThread调取执行结果task.get(),会有几种状况 if task 尚未被executor调度或正在执行中 阻塞当前线程,并加入到一个阻塞链表中waitNode else if task被其它Thread取消,并取消成功 或task处于中断状态 throw exception else if task执行完毕,返回执行结果,或执行存在异常,返回异常信息 若是此时有另一个线程调用task.get() 执行过程同上
总结下,FutureTask的状态流转过程,能够出现如下四种状况:
1. 任务正常执行并返回。 NEW -> COMPLETING -> NORMAL
2. 执行中出现异常。NEW -> COMPLETING -> EXCEPTIONAL
3. 任务执行过程当中被取消,而且不响应中断。NEW -> CANCELLED
4.任务执行过程当中被取消,而且响应中断。 NEW -> INTERRUPTING -> INTERRUPTED