线程池
SingleThreadExecutor() :单例的线程池
FixedThreadPool():定长的线程池
CachedThreadPool():缓存线程池
ScheduledThreadPool():支持定时及周期性任务执行。
阿里开发规范手册中指出的问题:
SingleThreadExecutor()和FixedThreadPool()
SingleThreadExecutor()进入源码:主要是默认的实现
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
SingleThreadExecutor()和FixedThreadPool()底层都是实现的LinkedBlockingQueue队列的阻塞方法,当线程不够用的时候所有的Runnable对象就会在队列中排队。请求的堆积线程会消耗大量的内存,甚至会引发OOM。
CachedThreadPool()和ScheduledThreadPool()
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); }
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory); }
SynchronousQueue<Runnable>和DelayedWorkQueue()随着任务数的增加会创建无数的线程,最后OOM。
当我们使用线程池的时候,注意一下这些问题。
基本线程的创建:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { }
7个参数:
corePoolSize:核心线程的数量
maximumPoolSize:最大线程的数量,核心线程数 + 非核心线程数
keepAliveTime:非核心线程池空闲后最大存活时间,如果设置allowCoreThreadTimeOut = true,则会作用于核心线程
TimeUnit :存活的时间单位
workQueue:当线程都在工作时,采用何种队列储存请求的Runnable对象。
threadFactory:这个参数没啥意义,给线程取名字
handler:当线程异常后需要作出的反应。
锁:
锁的类型:
可重入锁:在执行对象中所有同步方法不用再次获得同类型的锁
可中断锁:在等待获取锁过程中可中断
公平锁:按照线程的到来的先后顺序获得锁
读写锁:读锁可多线程一起读。写锁,只能一个线程操作,不可读
在加上数据库层面的 悲观锁和乐观锁
悲观锁:使用悲观锁的时,数据库中的这个行记录是不可用的
乐观锁:增加version字段,每次更改增加version值,通过判断version值来判断是否修改
Lock: 可重入锁,可中断锁,可以设置是否为公平锁,
Lock lock = new ReentrantLock(true);
ReentrantLock是Lock的实现类,只有可重入的实现类。如果不传入true则默认是非公平锁。非公平锁效率更高。主要方法:
void lock();获取锁,如果不可用则等待
void unlock():释放锁
boolean tryLock(); 尝试获取锁,如果能获取则为true
boolean tryLock(long time, TimeUnit unit) 时间约束,多长时间内能否获取锁
void lockInterruptibly() :可打断锁,如果在获取锁的时候进入等待,则可用方法将现场打断放弃等待
在使用lock加锁的时候一定要在finally中释放锁,不然这个锁会一致被异常线程占用,别的线程不可用。
synchronized与Lock的区别:参考博客中的图
读写锁:
ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); readWriteLock.readLock().lock(); readWriteLock.writeLock().lock(); readWriteLock.readLock().unlock(); readWriteLock.writeLock().unlock();
最要的方法就是readLock和writerLocak
readLock获取读锁,当使用读锁的时候,线程可以并发执行。当使用写锁的时候,所有的线程都需要排队直到写锁释放。
如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。读锁和写锁是互斥的。
这篇文章主要是自己的学习过程中的一个总结,所以写的比较粗糙,不够仔细。推荐的两篇博客都写的挺好的,有兴趣可以看一下。
努力吧,皮卡丘。