一、线程池
1,创建线程池
可以通过ThreadPoolExecutor来创建一个线程池。
int corePoolSize = 0;
int maximumPoolSize = 0;
long keepAliveTime = 0;
TimeUnit unit = null;
BlockingQueue<Runnable> workQueue = null;
ThreadFactory threadFactory = null;
RejectedExecutionHandler handler = null;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory,handler);
2,创建线程池的参数说明:
2.1 corePoolSize
线程池中的核心线程数;
如果有任务还为执行,并且当前线程数小于核心线程数,则会创建一个核心线程。
2.2 maximumPoolSize
线程池中的最大线程数。
如果线程池中没有空闲的线程来执行任务队列中的任务时,并且当前线程池的线程数小于最大线程数时,则会创建一个线程。
2.3 keepAliveTime
空闲线程的存活时间
2.4 unit(TimeUnit)
空闲线程存活时间的单位
2.5 workQueue(BlockingQueue<Runnable>)
用于缓存任务的队列
2.6 threadFactory(ThreadFactory)
用于创建线程的线程工厂
2.7 handler(RejectedExecutionHandler)
拒绝策略:当workQueue已满时,并且线程池中的线程数达到maximumPoolSize时,线程池拒绝添加新任务时采取的策略。
3,提交一个任务到线程池
有两个方法可以向线程池提交任务:execute(),submit()
3.1 execute()
该方法用于提交不需要有返回值的任务。
threadPoolExecutor.execute(()->{
// 需要执行的业务逻辑
});
3.2 submit()
该方法用于提交需要有返回值的任务。
会返回一个Future对象,使用这个对象的get()方法来获取返回值。
get()会阻塞当前线程直到任务执行完成或达到等待超时时间。
Future<?> future = threadPoolExecutor.submit(() -> {
// 需要执行的业务逻辑
return null;
});
try {
// 获取返回值
Object o = future.get();
} catch (InterruptedException e) {
e.printStackTrace();// 中断异常
} catch (ExecutionException e) {
e.printStackTrace();// 执行异常
}
4,关闭线程池
shutdown()和 shutdownNow()方法
4.1 shutdown()
线程池拒接收新提交的任务,然后将线程池的状态设置成SHUTDOWN状态,中断所有没有正在执行任务的线程,同时等待线程池里的任务执行完毕后关闭线程池。
4.2shutdownNow()
线程池拒接收新提交的任务,然后将线程池的状态设置成 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,同时立马关闭线程池,线程池里的任务不再执行。
如果任务不一定需要执行完,则可以调用这个方法来关闭线程池。
二、线程安全的集合
1,Vector
使用方法和ArrayList类似。
2,HashTable
使用方法和HashMap类似
3,ConcurrentHashMap
线程安全的性能较好的Map集合
4,CopyOnWriteArrayList
线程安全的性能较好的List集合
5,CopyOnWriteArraySet
线程安全的性能较好的Set集合
6,Collections包装方法
Collections针对每种集合都声明了一个线程安全的包装类
例如:
List<E> synArrayList = Collections.synchronizedList(new ArrayList<E>());
Set<E> synHashSet = Collections.synchronizedSet(new HashSet<E>());
Map<K,V> synHashMap = Collections.synchronizedMap(new HashMap<K,V>());
7,其他集合
ConcurrentSkipListMap、ConcurrentSkipListSet、ConcurrentLinkedQueue、ConcurrentLinkedDeque等
三、线程安全的单例模式
public class SingleDemo {
// 这里使用volatile关键字修饰后,可以避免出现第一个进入同步代码块的线程将instance指向还未初始化对象的内存空间
private volatile static SingleDemo instance = null;
private SingleDemo(){}
public static SingleDemo getInstance(){
if(instance==null){// 并发访问先判断是否为空,如果为空则其中一个线程获取到锁
synchronized (SingleDemo.class){
// 当第一个线程创建了对象都,其他线程进来同步代码块中,判断实例是否为空,就不会再创建一次
if(instance==null){
instance = new SingleDemo();
}
}
}
return instance;
}
}
四、死锁
/**
* 死锁
* 线程之间相互持有对方所需要的锁对象,造成两个线程相互等待锁死
*/
public class DeadLockDemo {
private static Object a = new Object();
private static Object b = new Object();
public static void main(String[] args){
// 线程A得到锁a,然后还要得到锁b
// 但是此时,锁b被线程b持有,线程A要等待线程B释放锁b
new Thread(()->{
synchronized (a){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){
System.out.println("1");
}
}
},"ThreadA").start();
// 线程B得到锁b,然后还要得到锁a
// 但是此时,锁a被线程A持有,线程B要等待线程A释放锁a
new Thread(()->{
synchronized (b){
synchronized (a){
System.out.println("2");
}
}
},"ThreadB").start();
}
}