Java之线程总结
2017-12-01 16:39
239 查看
线程vs进程
进程是资源分布的基本单位,一个进程中可以有多个线程,多个线程可以共享进程资源,在Java进程中,堆是共享的,因此需要保证线程安全,可通过-Xms指定Heap初始化大小,-Xmx指定Heap的最大值。线程是调度的基本单位,直接理解为可获取CPU时间片执行的代码,每个线程也有私有栈,默认大小为1M,太小会报StackOverFlow, 可通过-Xss指定栈大小,也可以new Thread是通过参数指定大小。
jinfo pid可查看进程启动信息、环境变量、classpath的信息。
jmap -heap pid可查看堆内部分布情况。
线程产生
定义线程,也就是定义获取CPU时间片时执行的代码,java中实现线程又两种方式:继承Thread类
实现Runabe接口
继承Thread类
首先看看Thread类主要方法://构造函数,指定Thread初始化属性 public Thread(); public Thread(Runnable target); public Thread(Runnable target, String name); public Thread(String name); public Thread(ThreadGroup group, Runnable target); ...... //获取当前正在运行线程对应的Thread对象 public static native Thread currentThread(); //打印堆栈 public static void dumpStack(); public ClassLoader getContextClassLoader(); public final void setPriority(int newPriority); //设置内部未被捕获的异常处理类 public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh); //调用sleep一定是当前正在运行的线程,所以为staic,与具有实例无关,yeid方法同理 public static native void sleep(long millis) throws InterruptedException; //释放CPU public static native void yield(); public void interrupt(); public boolean isInterrupted(); //等待线程结束 public final void join() throws InterruptedException; //线程执行体,target为Runnable public void run() { if (target != null) { target.run(); } } //启动线程方法,os内部会创建一个线程,与当前Thread实例关联,并且将此线程放入可调度执行队列中 public synchronized void start(); //设置守护线程 public final void setDaemon(boolean on);
Thread类提供了获取线程属性、ClassLoader、控制线程生命周期内各状态相互转换以及跟OS交互获取正在运行线程信息的相关函数。
new Thread Object时可指定线程名、线程执行逻辑、stacksize、线程组信息,也可采用默认值,通过set方法进行熟悉设置,同样可通过get方法获取这些属性,进程内每个线程都会分配一个id.
继承Thread类时,需重写run方法,然后调用start方法。
class AThread extends Thread { public void run() { // thread logic . . . } } Thread p = new AThread(); p.start();
实现Runabe接口
class ARun implements Runnable { public void run() { // thread logic . . . } } Runnable p = new ARun(); new Thread(p).start();
两种方式其实都是重载run()方法,在run方法内部定义执行逻辑,两种方式最终都需要生成一个Thread实例,然后调用start()方法让OS启动线程,最终调用Thread实例的run方法,start方法是线程启动的唯一入口。
一般采用实现Runnable方式,这样可以继承其他类,更加灵活。
FutureTask
FutureTask实现了Runnable接口,Thread.start方法最终会调用FutureTask.run()方法,因此也属于继承Runnable方式。
FutureTask同时还实现了Future接口,因此可以更多线程执行信息。
线程阻塞
阻塞后的线程不能被调度执行,java中有几种阻塞线程的方式:synchronized同步阻塞
Object的wait方法等待阻塞
Thread sleep方法睡眠阻塞
join方法产生join阻塞
IO阻塞
wait后会释放锁对象,sleep不会释放线程持有的锁对象。
阻塞中的线程退出阻塞有两种方式:
条件满足后退出阻塞比如sleep时间到,synchronized获取到锁等
线程被中断退出阻塞,但是synchronized阻塞是无法被中断的,reentrantLock.lock()也无法被中断,reentrantLock.lockInterruptibly()可被中断
线程中断
通过Thread实例的interrupt()方法可中断线程,设置线程中断标志位为true,线程检测到被中断后,如何响应取决与其自身根据JavaDoc对interrupt方法的解释:
如果线程处于wait、sleep以及jion三个方法引起的阻塞,那么会将线程的中断标志重新设置为false,这些方法抛出一个InterruptedException
如果是java.nio.channels.InterruptibleChannel进行的io操作引起的阻塞,则线程会收到一个ClosedByInterruptedException,但不会改变中断标志
如果是轮询(java.nio.channels.Selectors)引起的线程阻塞,则立即返回,不会抛出异常
如果运行中的线程被interrupt()则仅仅是将标志位设为true,当线程再次处于阻塞状态后发现标志为true时会按上面三种情况进行处理,当然如果一直没有被阻塞,则不会有任何影响,但一般情况下会通过isInterrupted()判断线程是否被中断,如果中断则结束线程。
关于中断更详细参考:
https://www.cnblogs.com/onlywujun/p/3565082.html
主动放弃cpu时间片
Thread.yield()静态方法,是否执行权只有对当前正在执行的线程有效,因此是静态方法才有意义。public static native void yield()
线程结束
Thread本身有一个stop方法,但是强制关闭,无任何清理动作,已Deprecated,目前有两种正确关闭方式:volatile共享变量,线程中检查共享变量状态决定是否退出
共享变量的方式在线程阻塞时无法检查到变量值,但阻塞时可检测到interrupt标志,因此可通过检测interrupt标志或是否捕获到InterruptException决定是否退出线程
线程同步
线程同步基本都是基于锁或Queue,可参考锁总结Daemon线程
守护线程,后台服务线程,为非Daemon线程服务,当非守护线程结束后才退出,比如gc线程线程组
Thread构造函数中可以指定线程组,同一个线程组具有一些相同线程属性,构造Thread时可以从线程组继承这些属性,比如Priority,isDaemon,ClassLoader等。线程池
Executor
抽象任务的执行,解耦任务的提交与调度执行,因此常用于替代new Thread(Runnable).start().public interface Executor { //执行一个Runnable对象,并没表名是如何执行,一个线程or线程池,甚至同步执行 void execute(Runnable command); }
ExecuteService
相对Executor,提供了提交任务以及关闭任务的接口public interface ExecutorService extends Executor { void shutdown(); List<Runnable> shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; //通过submit获取Future <T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
ThreadPoolExecutor
实现了ExecuteService的线程池,因此可以真正起线程执行提交的task.//corePoolSize最少线程数,maximumPoolSize最大线程数,keepAliveTime超过corePoolSize的线程空闲时间,workQueue执行线程存放的队列 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue);
ScheduledExecutorService
增加了定期和延时调度的接口public interface ScheduledExecutorService extends ExecutorService { public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit); public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit); public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit); }
ScheduledThreadPoolExecutor
实现了ScheduledExecutorService,可以周期性执行任务。public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, new DelayedWorkQueue()); }
Executors
Executors是线程池工厂,提供了创建线程池的接口,利用Exectors创建各种类型线程池,我们负责向线程池提交任务即可。//线程池大小为1 public static ExecutorService newSingleThreadExecutor(); //线程池大小为nThreads public static ExecutorService newFixedThreadPool(int nThreads); //不限定线程池大小 public static ExecutorService newCachedThreadPool(); //指定scheduledPoll的corePoolSize, 不限定poll上线 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
ForkJoinPool
实现了ExecutorService,用于将一个任务切分成多个小任务执行,然后将执行结果汇总,用于执行可切分的任务。Callable
Runnable无返回值,Callable是提供返回值类型任务,Runnable其实是返回值为null的Callablepublic interface Callable<V> { //返回V类型值 V call() throws Exception; }
Callable是如何被执行?其实提交后内部将包装成FutureTask, 而FutureTask是Runnable,可以被线程执行,FutureTask的run()实现中调用了Callable.call()方法。
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
实现了RunnableFuture接口,即同时实现了Runnable和Future接口,因此可以提交到线程池中执行,其实不管是Runnable还是Callable,最终都被封装成FutureTask通过ExecuteService.submit方法提交执行。因为实现了Future接口,因此可以通过FutureTask获取线程池中任务的执行状态,获取返回值等。java.util.concurrent.atomic.Atomic类
为方便多线程下数据安全,JDK对一些基本类型提供了Atomic类,可原子性操作Atomic类型变量, 内部是volatile变量加CAS操作。AtomicBoolean
public final boolean get(); public final void set(boolean newValue); public final void lazySet(boolean newValue); public final boolean getAndSet(boolean newValue); public final boolean compareAndSet(boolean expect, boolean update); public boolean weakCompareAndSet(boolean expect, boolean update);
AtomicInteger
除了AtomicBoolean提供了方法外,还增加了可加减相关的原子操作。public final int incrementAndGet() public final int decrementAndGet() public final int getAndIncrement() public final int getAndDecrement() public final int addAndGet(int delta) public final int getAndAdd(int delta)
AtomicLong
与AtomicInteger类似AtomicIntegerArray
对数组中的某个元素实现了原子操作,接口与AtomicInteger类似,只是多了个参数index.public final int getAndSet(int i, int newValue); public final boolean compareAndSet(int i, int expect, int update); public final int getAndIncrement(int i);
AtomicLongArray
与AtomicIntegerArray类似AtomicReference
包装了volatile 引用类型变量,对引用原子操作AtomicReferenceArray
同理,AtomicReferenceArray接口中带有参数indexAtomicMarkableReference
维持一个引用和对应的boolean型标志作为一个整体的原子操作AtomicStampedReference
与AtomicMarkableReference类似,只是与Reference作为整体的类型为integerThreadLocal
线程私有的变量,即每个线程都有一份数据,一般这种数据初次赋值后就不会再改变,比如userid等。public class ThreadLocal<T> { public ThreadLocal(); //构造是重写此方法 protected T initialValue() { return null; } public T get(); public void set(T value); public void remove(); }
通过源码很容量理解ThreadLocal实现原理,在每个Thread对象中有个成员变量ThreadLocal.ThreadLocalMap,此Map中存储了ThreadLocal class变量到到线程持有私有数据的映射,因此,对每个Thread Object,ThreadLocal.ThreadLocalMap是唯一的,不会共享,并且可存储多个不同类型私有数据。
一般ThreadLocal变量声明为private static,ThreadLocal 实例所有线程共享,只是使用其hashKey到每个线程私有的Map中索引到具体位置,然后存放私有数据。
java.util.SimpleDateFormat内部使用了成员变量做缓冲,是非线程安全的,多线程时作为成员变量需变成ThreadLocal保证线程安全,要不然会出现时间顺序错乱。
并发编程指南
http://www.importnew.com/26461.html相关文章推荐
- 黑马程序员-----java线程学习与总结
- Java中的线程Thread总结
- java线程安全总结
- Java线程安全总结
- Java【多线程知识总结(1)】用Thread类创建线程
- Java中线程应用总结
- java线程安全总结
- java线程之基础学习总结(三)
- Java中线程总结
- Java基础学习总结(68)——有关Java线程方面的面试题
- java基础学习总结—— 线程
- Java多线程总结之线程安全队列Queue
- java线程总结
- java多线程总结二:后台线程(守护线程)
- java 线程总结
- [转]Java线程安全总结
- java中进程与线程_三种实现方式总结(必看篇)
- Java线程学习和总结(线程的概念)
- 关于Java中停止线程执行的方法总结
- 黑马程序员:Java基础总结----线程间通信