JUC学习!
JUC
1、what?
JUC就是java.util.concurrent下面的类包,专门用于多线程的开发。
2、why?
解决多线程、高并发
3、how?
||
||
﹀
point1:volatile关键字与内存可见性;
question1:多个线程共享资源时,彼此不可见; 可以给资源加锁,但是加锁,线程会阻塞,效率较低;如何在不使用锁的情况下,更新资源,即给资源加上volatile关键字。
point2:原子性
原子变量:Java提供了原子变量,在java.util.concurrent.atomic包下; 使用CAS(Compare And Swap)来保证原子性;当内存值==预估值时,我才会把更新值更新到内存。但此时会出现一个ABA问题,即一个线程修改变量A,为B再改回A,此时CAS察觉不到,被修改了,当然基本数据类型是不要紧的,但如果是引用数据呢?这个对象中有多个变量,我不知道有没有被修改。因此,增加版本号就成了一个很好的选择;
point3:锁分段机制
在java.util.concurrent包中提供了多种并发容器类来改进同步容器类的性能。其中最主要的就是ConcurrentHashMap。 ConcurrentHashMap采用了分段锁机制,是一个线程安全的hash表,我们知道HashMap不是线程安全的,HashTable加了锁,是线程安全的,因此他效率低。而ConcurrentHashMap默认分成了16个segment,每个segment对应一个hash表,且有自己独立的锁。所以每一个线程访问一个segment,就可以并行访问了,这大大的提高了效率;
point4:锁
1、传统锁synchronized关键字:
a、锁代码块、锁类; b、当这个关键字锁到一个带有static的方法时,锁的是这个类模板。
- class X{
- public Synchronized void f(){
- methods body...
- }
- }
2、Lock接口:
是在juc包下的,首先要声明一个可重入锁; 有三个实现类:ReentrantLock(可重入锁 常用)、 ReentrantReadWriteLock.ReadLock(读锁)、ReentrantReadWriteLock.WriteLock(写锁)
- class X{
- Lock lock = new ReentrantLock();
- public void f(){
- lock.lock;
- try{
- method body.....
- }finally{
- lock.unlock;
- }
- }
- }
1、公平锁:十分公平,先来后到;
2、非公平锁:十分不公平,可以插队(默认);
3.lock与Synchronized的区别
- >1、Synchronized是内置关键字;Lock是接口;
>2、Synchronized无法判断获取锁的状态;Lock可以判断;
>3、Synchronized会自动释放锁;Lock需要手动释放,不释放会出现死锁现象;
>4、Synchronized(当a线程获取锁并阻塞,b线程会一直等);Lock不会这样(tryLock()方法,当目前可以获取锁时返回true,反之false)
>5、Synchronized是可重入锁、不可中断、非公平锁;Lock可重入、可判断锁、公平性(可以自定义),扩展性更好;
>6、Synchronized可以锁少量同步代码;Lock可以锁大量的同步代码;
4、锁的类型
可重入锁:拿到外面的锁后,会自动拿到里面的锁;
自旋锁:
point5:生产者和消费者问题
1、线程之间通信问题:生产者和消费者问题!
2、三部曲:判断是否需要等待 this.wait() 、业务、通知 this.notifyAll()
3、线程的虚假唤醒问题,将if()改为while()判断;
- Condition condition = lock.newCondition();
condition.await();//等待
condition.signal();//通知
Condition实现精确通知
- Condition condition1 = lock1.newCondition();
Condition condition2 = lock2.newCondition();
Condition condition3 = lock3.newCondition();
例子:当前condition1,要通知condition3,则要condition3.signal();
point6:8锁现象
1、多个线程使用同一个对象,多个线程就是使用一把锁,先调用的先执行!
2、多个线程使用同一个对象,多个线程就是使用一把锁,先调用的先执行,即使在某方法中设置了阻塞。
3、多个线程,有的线程有锁,有的线程没锁,两者之间不存在竞争同一把锁的情况,先后执行顺序是随机的。
4、a被 synchronized 修饰的方法,锁的对象是方法的调用者;b调用者不同,它们之间用的不是同一个锁,相互之间没有关系。
5、被 synchronized 和 static 同时修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。线程之间是顺序执行。
锁Class和锁对象的区别:
a、Class 锁 ,类模版,只有一个;
b、对象锁 , 通过类模板可以new 多个对象。
6、被 synchronized 修饰 和 static 修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。
Class锁是唯一的,所以多个对象使用的也是同一个Class锁。
如果全部都锁了Class,那么这个类下的所有对象都具有同一把锁。
7、被 synchronized和static修饰的方法,锁的对象是类的class对象!唯一的同一把锁;
只被synchronized修饰的方法,是普通锁(如对象锁),不是Class锁,所以进程之间执行顺序互不干扰。
解释见:https://blog.csdn.net/makyan/article/details/104524725
point7:集合不安全
异常:ConcurrentModificationException,并发修改异常;
解决方案:
List<String> list =new ArrayList<String>();
1、List<String> list = new Vector(); --->被synchronized修饰的方法效率很低;
2、List<String> list = Collections.syncronizedList(new ArrayList<>());
3、List<String> list = new CopyOnWriteArrayList<>();
Set<String> set = new HashSet<>();
1、Set<String> set = Collections.synchroniazedSet(new HashSet<>());
2、Set<String> set = new CopyOnWriteArraySet<>(); HashSet本质就是用了HashMap的key不重复来实现去重的;
Map<String,String> map = new HashMap<>();
1、Map<String,String> map = Collections.synchronizedMap<>();
2、Map<String,String>map = new ConcurrentHashMap<>();--->详情见point3
point8:Callable与Runable
先看一下两个接口的定义:
Callable
- public interface Callable<V> {
- V call() throws Exception;
- }
Runnable
- interface Runnable {
- public abstract void run();
- }
和明显能看到区别:
1.Callable能接受一个泛型,然后在call方法中返回一个这个类型的值。而Runnable的run方法没有返回值
2.Callable的call方法可以抛出异常,而Runnable的run方法不会抛出异常。
point9:常用辅助类
1、CountDownLatch 减法计数器
- CountDownLatch count=new CountDownLatch(5);
- for (int i = 1; i <=5 ; i++) {
- new Thread(()->{
- System.out.println(Thread.currentThread().getName()+"go out");
- count.countDown();//count--
- },String.valueOf(i)).start();
- }
- count.await();//等待计数器归零,然后向下执行
- System.out.println("over");
2、CyclicBarrier 加法计数器(集齐七龙珠召唤神龙)
- CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()-> System.out.println("召唤神龙"));
- for (int i = 1; i <=7 ; i++) {
- final int temp = i;
- new Thread(()->{
- System.out.println(Thread.currentThread().getName()+"收集第"+temp+"个龙珠");
- try {
- cyclicBarrier.await();//等待
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (BrokenBarrierException e) {
- e.printStackTrace();
- }
- },String.valueOf(i)).start();
- }
3、Semaphore 流量
停车位,限流;三个车位,六辆车;
- //有3个停车位,有六辆车
- Semaphore semaphore = new Semaphore(3);
- for (int i = 1; i <=6 ; i++) {
- new Thread(()->{
- try {
- semaphore.acquire();//获取许可
- System.out.println(Thread.currentThread().getName()+"抢到车位");
- TimeUnit.SECONDS.sleep(2);
- System.out.println(Thread.currentThread().getName()+"离开车位");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally{
- semaphore.release();//释放信号
- }
- },String.valueOf(i)).start();
- }
point10:读写锁
ReentrantReadWriteLock 看一下jdk源码:
point11:阻塞队列BlockingQueue
1、使用情况:
2、四种API:
方法 | 抛出异常 | 有返回值,不抛异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put | offer(Object o,Long timeout,TimeUnit t) |
移除 | remove() | poll() | take | poll(Long timeout,TimeUnit t) |
检测队首 | element() | peek |
- /**
- * 抛出异常
- */
- public static void test1(){
- ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
- System.out.println(arrayBlockingQueue.add("a"));
- System.out.println(arrayBlockingQueue.add("b"));
- System.out.println(arrayBlockingQueue.add("c"));
- //检查队首元素
- System.out.println(arrayBlockingQueue.element());
- System.out.println("=================");
- System.out.println(arrayBlockingQueue.remove());
- System.out.println(arrayBlockingQueue.remove());
- System.out.println(arrayBlockingQueue.remove());
- }
- /**
- * 有返回值,不抛出异常
- */
- public static void test2(){
- ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
- System.out.println(blockingQueue.offer("a"));
- System.out.println(blockingQueue.offer("b"));
- System.out.println(blockingQueue.offer("c"));
- System.out.println(blockingQueue.offer("c"));
- //查看队首元素
- System.out.println(blockingQueue.peek());
- System.out.println("=======================");
- System.out.println(blockingQueue.poll());
- System.out.println(blockingQueue.poll());
- System.out.println(blockingQueue.poll());
- System.out.println(blockingQueue.poll());
- }
- /**
- * 阻塞等待
- */
- public static void test3(){
- ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
- try {
- blockingQueue.put("a");
- blockingQueue.put("b");
- blockingQueue.put("c");
- System.out.println("===");
- blockingQueue.take();
- blockingQueue.take();
- blockingQueue.take();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- /**
- * 超时等待
- */
- public static void test4(){
- ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
- try {
- System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
- System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
- System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
- System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
- System.out.println("====================");
- System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
- System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
- System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
point12:SynchronousQueue同步队列
同步队列和阻塞队列不同,同步队列不存储元素; 往里面put一个元素,必须要先take取出来;
point13:线程池(重点)
1、为什么要用线程池?线程池的好处?
- >a、降低资源的消耗,不用频繁的创先销毁线程;
- >b、提高响应速度
- >c、方便管理
2、三大方法、七大参数、四种拒绝策略
A、三大方法
- //不要使用Executors工具类来创建线程池,用最原生的ThreadPoolExecutor;
- ExecutorService threadPool= Executors.newSingleThreadExecutor();//单个线程
- ExecutorService threadPool1=Executors.newFixedThreadPool(5);//固定线程
- ExecutorService threadPool2= Executors.newCachedThreadPool();//可变线程,遇强则强,遇弱则弱;
- try{
- for(int i=0;i<10;i++){
- //使用线程池创建线程
- threadPool.execute(()->{
- System.out.println(Thread.currentThread().getName()+"OK");
- })
- }
- }finally{
- threadPool.shoutdown();//线程池用完,程序关闭,最后关闭线程池
- }
B、七大参数
ThreadPoolExecutor
- public ThreadPoolExecutor(int corePoolSize,//核心线程数
- int maximumPoolSize,//允许最大线程数
- long keepAliveTime,//当线程数大于核心线程数时,这就是多余空闲线程的存活时间
- TimeUnit unit,//时间单位
- BlockingQueue<Runnable> workQueue,//在执行任务之前用于保留任务的队列。 此队列将仅保存execute方法提交的Runnable任务
- ThreadFactory threadFactory,//执行过程中创建新的线程所需要的工厂
- RejectedExecutionHandler handler//拒绝策略,当线程满了、阻塞队列也满了时会执行的策略
- )
C、4种拒绝策略RejectedExecutionHandler:
- new ThreadPoolExecutor.AbortPolicy();//银行满了,还有人进来,不处理,抛异常;
- new ThreadPoolExecutor.CallerRunsPolicy();//哪里来的回哪里
- new ThreadPoolExecutor.DiscardOldestPolicy();//队列满了尝试和最早的竞争,不会抛出异常;
- new ThreadPoolExecutor.DiscardPolicy();//队列满了丢掉任务,不会抛异常;
3、最大线程怎么定义(调优)
A、CPU密集型 获取当前CPU的核数:Runtime.getRuntime().availableProcessors() 最大线程==当前CPU核数
B、IO密集型 判断程序中十分耗IO的线程 最大线程 > 十分耗IO的线程数
point14:四大函数式接口(简化编程模型
函数型接口
- Function function = (str)->{ return str;};//有参数、有返回值
断定型接口
- Predicate<String> predicate =(str)->{return str.isEmpty();};//有参数,返回布尔值
消费性接口
- Consumer<String> consumer =(str)->{ System.out.println("已消费"); };//有参数,无返回值
供给型接口
- Supplier supplier=()->{return "你好";};//无参数,有返回值
ponit15:Stream流
- /* 筛选条件
- * 1、id为偶数
- * 2、年龄大于22
- * 3、名字转为大写
- * 4、倒序
- * 5、输出一个
- */
- User u1 =new User(1,"a",20);
- User u2 =new User(2,"b",21);
- User u3 =new User(3,"c",22);
- User u4 =new User(4,"d",23);
- User u5 =new User(5,"e",24);
- User u6 =new User(6,"f",25);
- List<User> list = Arrays.asList(u1, u2, u3, u4,u5,u6);
- list.stream().filter(u->{return u.getId()%2==0;})
- .filter(u->{return u.getAge()>22;})
- .map(u->{return u.getName().toUpperCase();})
- .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
- .limit(1)
- .forEach(System.out::println);
point16:ForkJoin
把一个大任务拆成多个小任务并行执行 例如:计算1-10亿内的数累加 使用ForkJoin可以提升速度,使用Stream流会更快
point17:异步回调
ComletableFuture
JUC学习!的更多相关文章
- JUC学习笔记(六)
JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html JUC学习笔记(二)https://www.cnblogs.com/lm66/p/1511 ...
- JUC学习笔记(五)
JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html JUC学习笔记(二)https://www.cnblogs.com/lm66/p/1511 ...
- JUC学习笔记(四)
JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html JUC学习笔记(二)https://www.cnblogs.com/lm66/p/1511 ...
- JUC学习笔记(三)
JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html JUC学习笔记(二)https://www.cnblogs.com/lm66/p/1511 ...
- JUC学习笔记(二)
JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html 1.Lock接口 1.1.Synchronized 1.1.1.Synchronized关 ...
- JUC学习笔记——进程与线程
JUC学习笔记--进程与线程 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的进程与线程部分 我们会分为以下几部分进行介绍: 进程与线程 并发与并行 同步与异步 线程详解 进程与线程 ...
- JUC学习笔记——共享模型之管程
JUC学习笔记--共享模型之管程 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的管程部分 我们会分为以下几部分进行介绍: 共享问题 共享问题解决方案 线程安全分析 Monitor ...
- JUC学习笔记——共享模型之内存
JUC学习笔记--共享模型之内存 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的内存部分 我们会分为以下几部分进行介绍: Java内存模型 可见性 模式之两阶段终止 模式之Balk ...
- JUC学习记录
先附上学习的博客地址:http://blog.csdn.net/cx8122389/article/details/70049425, 具体见该博客 Java JUC 简介 在Java 5.0 提供了 ...
- JUC学习笔记--Atomic原子类
J.U.C 框架学习顺序 http://blog.csdn.net/chen7253886/article/details/52769111 Atomic 原子操作类包 Atomic包 主要是在多线程 ...
随机推荐
- python 迁移虚拟环境
1.在源环境中获取包列表(新建文件夹whls) #cd 虚拟环境目录下的\scripts,cmd acitivate # 下载清单到requirements.txt,切换到whls目录 pip fre ...
- 用深度学习模型Word2Vec探索《红楼梦》人物关系
先来看一看结果,发现: 1.贾宝玉和袭人的关系最近. 2.薛宝钗和自己的妈妈关系最近. 3.贾宝玉和林黛玉逼格比较统一,薛宝钗属于独树一帜的逼格调性. 4.大观园中可以看到邢岫烟经常出没... 还有更 ...
- Redis如何找出并快速删除亿级指定前缀的key
背景 由于Redis的单线程服务模式,命令keys *会阻塞正常的业务请求,不建议使用keys * pattern的方法进行查询,可能会使服务器卡顿而出现事故.如何获取指定的 key? 可以采用Red ...
- mysql增删改查json中的某个字段
创建表 1 CREATE TABLE t_json(id INT PRIMARY KEY, NAME VARCHAR(20), info JSON); 插入记录 1 INSERT INTO t_jso ...
- Java使用HSSFWorkbook生成Excel
HSSF 是Horrible SpreadSheet Format的缩写,也即"讨厌的电子表格格式". 也许HSSF的名字有点滑稽,就本质而言它是一个非常严肃.正规的API.通过H ...
- 单文件WSDL,非模块化
最近在使用CXF做WebService Sever端,接口与实现类不在一个包下. 实现类如下: 1 @WebService(serviceName = "Demo" 2 , tar ...
- JS篇(007)-事件委托是什么
答案:利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行! 解析: 1.那什么样的事件可以用事件委托,什么样的事件不可以用呢? 适合用事件委托的事件:click,mousedown,mou ...
- JS篇(003)-请用 js 去除字符串空格?
答案:replace 正则匹配方法.str.trim()方法.JQ 方法:$.trim(str)方法 解析: 方法一:replace 正则匹配方法 去除字符串内所有的空格:str = str.repl ...
- rpm制作(简)
yum -y install gcc prec-devel openssl-devel zlib-devel yum -y install rpm-build #生成工作目录 rpmdev-setup ...
- grpc start with python
pip install grpcio grpcio-tools syntax = "proto3"; service FutureData { rpc GetTick(ReqTic ...