1.创建多线程
2.synchronized线程同步机制
3.volatile关键字
Ø通俗地讲:volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时, 又会强迫线程将最新的值刷新到主内存。这样任何时刻,不同的线程总能看到该变量的最新值
volatile
1.对变量的写操作不依赖当前值。
2.该变量没有包含在具有其他变量的式子中
特别适合作为状态标识量
另一个使用场景就是 使用两次
4.线程的安全与不安全
5.java并发工具与连接池
juc CountDownLatch 倒计时锁
juc 之Semephore信号量
尝试获取许可 获取到了就执行 获取不到 直接放弃,上图就只能有三个线程执行。
juc之CyclicBarrier循环屏障
只解决一个问题,就是让所有线程同时执行。
使用场景;
用于多线程计算数据,最后合并计算结果的计算场景。
1.cpu评测软件。(所有线程同时开始)
2.零点零分双十一秒杀 (假设有20个奖品,设置20个线程去抽奖,在0点0分时必须同时跑,不能有先后顺序。)
3抢票软件。(在后车票开售时,就必须有300或500个线程同时跑进去抢票)
和countDownLatch区别:
countDownLatch:
1.计数器只能用一次。
2.实现一个或多个线程需要等待其他线程完成某项操作后,才能继续往下执行(描述的是一个或多个线程等待其他线程 关系)
CyclicBarrier :
1计数器可以重置(循环使用)
2.多个线程之间相互等待,知道所有线程都满足条件之后,才能继续执行后续操作(描述的是各个线程内部相互等待的关系,所以他能处理更复杂业务场景)。
juc之ReentrantLock重入锁(不太推荐使用)
比如我们拿到文件锁之后 跳出去做其他事,在回来还可以获得这个文件锁,就是可重入锁。
提高性能 是想尽办法 不让线程进入内核的阻塞状态。
总结 :
1.当只有少了竞争者时 synchronized (不会引发死锁,jvm 会自动解锁)是最佳选择
2.竞争者不少,但是线程增长的趋势是可以预估的这是推荐用ReentrantLock
juc 之Condition线程等待与唤醒
任何需要预先设置好顺序的线程都需要Condition的支持
juc之Callable&Future
JUC-Fork/Join(jdk7 用于并行执行的框架) 框架
Fork/Join 是将大任务 分拆为若干个小任务,最终汇总每个小任务的结果后得到大任务结果的框架。fork---分拆, join---合并。
采用的是 工作窃取算法(如下图)
假如需要做一个比较大的任务,可以把这个任务 分割个若干个互不依赖的小任务,为了减少线程间的竞争,把这些子任务分别放到不通的队列里,为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程处理A队列里的任务,但是有的线程会先把自己队列里的任务处理完,而其他线程队列里还有任务需要处理,于是先处理完的线程回去其他未完成的队列里窃取任务,而这时多个线程会访问同一队列,为了减少任务线程和被窃取任务的线程间的竞争,通常我们会使用双端队列,被窃取任务的线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾巴获取任务。
优点;充分利用线程进行并行计算,并减少了线程见得竞争
缺点: 在某些情况下还是存在竞争(比如双端队列里只有一个任务时),更加消耗系统资源。
局现性
1任务只能用fork和join来作为同步机制,如果使用了其他同步机制 工作线程就不能执行其他任务了。
2.所拆分的任务 不应该执行io操作(如读和写数据文件)
3.任务不能抛出检查异常,必须通过必要代码处理
代码:
类必须继承RecursiveTask<>类
其中Recursive是递归的意思,就是把大任务不断拆分成小任务。
下面例子做相加运算(从1加到100),假设相加任务很耗时,所以将任务分解多线程相加
流程,首先拆分子任务,之后让子任务各自执行,最后同过join 的方式合并结果
JUC-BlockingQueue
线程安全,主要用于生产者和消费者模型
线程池
ThreadPoolExecutor
如果运行线程数小于corePoolSize,直接创建新线程处理任务,如果线程池中线程数量大于corePoolSize且小于maximumpoolSize则只有当workQueue满了的时候 才会创建线程到maximumpoolSize值,来处理任务
HashMap与ConcurrentHashMap解析
HashMap
底层结构是 数组+链表
HashMap 有两个参数影响性能是初始容量(16)和加载因子(0.75)
加载因子是能达到最大容量,如16*0.75=12 当放入12个元素后HashMap就需要扩容为原来的2倍
HashMap 的寻址方式:
对于一个新插入或需要读取的数据,HashMap需要将其key按照一定计算规则计算出hash值并对数组长度进行取模,结果作为在查找中的index
单线程下rehash
多线程下rehash (扩容) 容易出现死循环
ConcurrentHashMap(jdk7,基于分段锁处理的)
ConcurrentHashMap 底层结构任然是数组和链表,与HashMap不同的是最外层不是一个大数组而是一个Segment 数组
与hashmap的不同点:
hashmap 线程不安全,如许key和value为空,不容许在遍历的时候修改
ConcurrentHashMap 线程安全 不如许key和value为空,如许遍历的时候修改,并且跟新对后面的遍历可见
java8下优化的ConcurrentHashMap 将底层数结构中的链表用了红黑树来提高并发性(默认并发数达到8时将链表变为红黑树)
juc之Atomic与CAS算法(乐观锁)
单例模式代码
/**
* 双重同步锁单例模式
* 限制程序 不让其发生指令重排(volatile 关键字可以限制不发生指令重排)
*/
public class SingletonExample5 {
//首先定义私有的构造方法(只有构造函数私有,才能保证外面不能通过new的方式不断创建对象出来)
public SingletonExample5() {
}
//1.memory=allocate 分配对象内存空间
//2.ctorInstance() 初始化对象
//3.Instance=memory 设置instance指向刚分配的内存
//在多线程下 jvm和cpu 优化,发生了指令重排
//1.memory=allocate 分配对象内存空间
//3.Instance=memory 设置instance指向刚分配的内存
//2.ctorInstance() 初始化对象
//下面 代码重排后 在线程a 执行第3步 给设置instance指向刚分配的内存
//但是 线程b 刚好执行到判断那步 就会直接返回instance对象,但是实际instance对象并没有初始化
//使用volatile可以限制指令重排 所以就是线程安全的
//单例对象 volatile+双重检测机制---》禁止指令重排
private volatile static SingletonExample5 instance=null;
//静态工厂模式(懒汉模式,就是第一次使用的时候创建)
public static SingletonExample5 getInstance(){
if(instance==null){//双重监测机制
synchronized(SingletonExample5.class){//同步锁
if (instance==null){
instance=new SingletonExample5();
}
}
}
return instance;
}
}
@Slf4j @ThreadSafe @Recommend public class SingletonExample7 { public SingletonExample7() { } public static SingletonExample7 getInstance(){ return Singleton..getInstance(); } private enum Singleton{ ; private SingletonExample7 singlenton; //jvm 保证只被调用一次 Singleton(){ singlenton=new SingletonExample7(); } public SingletonExample7 getInstance(){ return singlenton; } } }