(队列)阻塞队列 BlockingQueue
一个线程往里边放,另外一个线程从里边取的一个 BlockingQueue
BlockingQueue是一个接口常用实现类为:
*ArrayBlockingQueue:内存连续,查询快
*LinkedBlockingQueue:内存不连续,添加方便
实现类的各种操作:
操作原则:FIFO先进先出
初始化ArrayBlockingQueue:
BlockingQueue queue = new ArrayBlockingQueue(理论上是有界的但是可以设置为Integer.MaxValue);
初始化LinkedBlockingDeque:
BlockingQueue queue=new LinkedBlockingDeque();Linked队列,默认的容量上限是Integer.MaxValue
*具有优先级的阻塞队列PriorityBlockingQueue:
优先级队列可以实现排序。队列的内部工作原理:按照compareto的排序规则对元素重新进行了排列,然后FIFO的原则来取的
以传入Student对象为例:
BlockingQueue queue=new PriorityBlockingQueue ();
Student声明:
public class Student implements Comparable,Cloneable
public int compareTo(Student o) {
return o.score-this.score;
}(Map)并发 Map( 映射) ConcurrentMap
HashMap 线程不安全
HashTable 线程安全,底层实现的方式是所有的方法都加了同步代码块。但是问题是性能特别低
HashTable会锁住整表,当有一线程操作HashTalble,其他线程即不能读,也不能写
ConcurrentMap的实现原理:引入了分段锁(桶)的概念,分成了16个桶(segments).
这样,当线程在操作某一个key的时候,锁住的是这个key对应的桶。理论上来说,ConcurrentMap的性能是HashTable的16倍
定义:ConcurrentHashMap map=new ConcurrentHashMap();(Map)并发导航ConcurrentNavigableMap
实现自截取,反省类必须实现cloneable接口,并重写其方法
ConcurrentNavigableMap map=new ConcurrentSkipListMap();
ConcurrentNavigableMap headMap=map.headMap(3);
System.out.println(headMap.entrySet().size());//数字为2;
headMap(T toKey) 方法返回一个包含了小于给定 toKey 的 key 的子 map。
tailMap(T fromKey) 方法返回一个包含了不小于给定 fromKey 的 key 的子 map。
subMap() 方法返回原始 map 中,键介于 from(包含) 和 to (不包含) 之间的子 map。(线程锁)闭锁 CountDownLatch
闭锁(也叫线程递减锁)
知识点1:创建闭锁时,需要赋值一个初始计数量。
知识点2:await()会产生阻塞,当初始计数量=0时,阻塞放开
知识点3:countDown()每调用一次,计数量-1
主函数:
CountDownLatch cdl=new CountDownLatch(2);
new Thread(new GuoRunner(cdl)).start();
new Thread(new CaiRunner(cdl)).start();
cdl.await();
执行函数:
class GuoRunner implements Runnable{
private CountDownLatch cdl;
public GuoRunner(CountDownLatch cdl) {
this.cdl=cdl;
}
public void run() {
System.out.println(“锅买回来了”);
cdl.countDown();//声明的计数减一
}
}(线程锁)栅栏 CyclicBarrier
栅栏,可以实现线程同步效果。主要就是调用await()方法,将创建时赋值的初始计数器减为0.
赛马例子:
模拟两匹赛马(两个线程)
只有当两匹赛马都到达栅栏前,比赛才开始
主函数:
CyclicBarrier cb=new CyclicBarrier(2);
new Thread(new Horse1Runner(cb)).start();
new Thread(new Horse2Runner(cb)).start();
执行函数:
try {
//栅栏的await()方法会产生阻塞,此外,此方法被调用一次,初始计数器-1
cb.await();
}
//栅栏的await()方法会产生阻塞,此外,此方法被调用一次,初始计数器-1,直至减到0,栅栏放开
cb.await();(线程信息交换)交换机 Exchanger
交换机:用于两个线程之间信息的交互,主要通过交换机的exchange方法来实现的。
两个间谍传递暗号(两个线程)
通过交换机相互传递暗号
主函数:
Exchanger ex=new Exchanger();
new Thread(new Spy1(ex)).start();
new Thread(new Spy2(ex)).start();
执行函数:
Object spy1Info=ex.exchange(info);//只能两个线程交换数据(线程类)Callable
Callable类似于Runnable,一个类实现了Callable接口,就成为一个线程类
1.Callable的call方法可以抛异常,Runnable的run方法不可以
2.Callable的call方法可以有返回值,自己指定。run方法的返回值只能是void
3.call方法的返回值是可以拿到的,应用场景:可以返回值来做具体判断,做相关的业务逻辑控制
4.callable线程类只能通过线程池来启动。(不能通过new Thread().start()来启动)
实例:
ExecutorService es=Executors.newCachedThreadPool();
Future future=es.submit(new CallRunner());
System.out.println(“call方法的返回值:”+future.get());
es.shutdown();(线程池)执行器服务 ExecutorService
引入线程池,最重要的改善就是:避免线程的频繁创建和销毁
第一个参数:corePoolSize:线程池的核心线程数量,此线程数量一直会创建,直到达到指定的数量位置
第二个参数:maximumPoolSize:当核心线程和队列都满了之后,再有新的请求过来,会创建临时线程。
原则是:核心线程数+临时线程数<=MaxPoolSize
第三个参数: keepAliveTime:这个参数控制的是临时线程的存活时间(闲置时间),时间一过,临时线程被清理掉
unit:时间单位
第四个参数:workqueue:当核心线程都占用之后,再有新的请求过来,会将请求先存放到队列里等待出来。处理的原则是:FIFO
第五个参数:拒绝服务器助手:当核心满+队列满+临时满的时候,再有新的请求过来,这个请求会转给线程池的拒绝服务器助手
ExecutorService es=new ThreadPoolExecutor(
5, 10, 3000,TimeUnit.MILLISECONDS,new ArrayBlockingQueue(5),new RejectedExecutionHandler() {
//rejectedExecution 方法用于解决处理不了的请求
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(“服务器繁忙”);
}});
启动线程:
es.execute(new ExRunner());
大池子小队列newCachedThreadPool:- (0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue())
*特点:没有核心线程|临时线程无限|临时线程数量存活时间:60分钟|同步队列,队列里只能存储一个元素
*大池子小队列:好处是响应时间短,能够快速和良好的处理请求。能够充分利用cpu。
*隐患:如果请求数量大,而且都是长请求的话,可能会导致池子一直在创建新的线程,严重的后果会造成内存溢出。
*适用场景:高访问量且短请求场景
小池子大队列newFixedThreadPool: - nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,new
LinkedBlockingQueue() - 特点:只有核心线程|没有临时线程|因为没有临时线程,所以没有存活时间|队列是无界的
- 小池子大队列:应用场景:能够缓解服务器压力,将处理不了的请求存到队列里,供后续处理。
- 局限性:响应时间可能达不到立即响应。
- (0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue())
(锁) Java Lock 实现
重入锁默认用的是非公平锁策略,非公平锁的吞吐量要高于公平锁的吞吐量
重入锁潜在的风险:锁释放的问题,必须在finally代码块里做释放
sychronized的锁释放是jvm来做的
主函数:
//重入锁,如果是true 是公平锁策略
//默认是flase,非公平锁
ReentrantLock lock=new ReentrantLock();
new Thread(new Lock1Runner(lock)).start();
new Thread(new Lock2Runner(lock)).start();
执行函数:
lock.lock();
if(TestLock.name.equals(“李雷”)){
TestLock.name=”韩梅梅”;
TestLock.gender=”女”;
}else{
TestLock.name=”李雷”;
TestLock.gender=”男”;
}
lock.unlock();//在finally代码块执行!
读写锁:对锁实现细粒度的控制,能够达到只对写上锁,不影响读
lock.writeLock().lock();
lock.readLock().lock();
finally {
rwlock.writeLock().unlock();
}
finally {
rwlock.readLock().unlock();
}(元素类型)原子性布尔 AtomicBoolean
原子性类型能够实现原子操作,底层的实现原理是加锁,这个数字在进行数学计算时,会被锁,从而实现数据不会被错误的覆盖。
主函数:
public static AtomicInteger i=new AtomicInteger(0);
CountDownLatch cdl=new CountDownLatch(2);
new Thread(new AtomicRunner(cdl)).start();
new Thread(new AtomicRunner(cdl)).start();
cdl.await();
System.out.println(i);
执行函数:
for(int j=0;j<1000;j++){
TestAtomic.i.addAndGet(1);
}