为了账号安全,请及时绑定邮箱和手机立即绑定

java 多线程基础

标签:
Java

1.创建多线程

https://img1.sycdn.imooc.com//5bd7b5190001b6cd05350303.jpg

https://img1.sycdn.imooc.com//5bd7b5380001a9ed05580236.jpg



2.synchronized线程同步机制

https://img1.sycdn.imooc.com//5bd7b565000148be05580204.jpg

https://img1.sycdn.imooc.com//5bd7b5d700013ae705580302.jpg


3.volatile关键字

https://img1.sycdn.imooc.com//5bd7b5e60001abc505580323.jpg

https://img1.sycdn.imooc.com//5bd7b6010001628505580365.jpg

https://img1.sycdn.imooc.com//5bd7b62500010c2805580352.jpg

Ø通俗地讲:volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时, 又会强迫线程将最新的值刷新到主内存。这样任何时刻,不同的线程总能看到该变量的最新值

https://img1.sycdn.imooc.com//5bd7b64d0001d1a108420531.jpg



volatile 

1.对变量的写操作不依赖当前值。

2.该变量没有包含在具有其他变量的式子中

特别适合作为状态标识量

另一个使用场景就是 使用两次




4.线程的安全与不安全

https://img1.sycdn.imooc.com//5bd7b6d300015a3605580255.jpg



5.java并发工具与连接池


juc CountDownLatch 倒计时锁

https://img1.sycdn.imooc.com//5bd7b7160001346505580350.jpg

https://img1.sycdn.imooc.com//5bd7b72c0001a1ca05580353.jpg

https://img1.sycdn.imooc.com//5bd7b75f00019a3d05580301.jpg



juc 之Semephore信号量

https://img1.sycdn.imooc.com//5bd7b77200017bbe05570261.jpg

https://img1.sycdn.imooc.com//5bd7b79f0001505e05580227.jpg

https://img1.sycdn.imooc.com//5bd7b7ac0001109a05580271.jpg

尝试获取许可 获取到了就执行 获取不到 直接放弃,上图就只能有三个线程执行。


juc之CyclicBarrier循环屏障

https://img1.sycdn.imooc.com//5bd7b7de0001990705580280.jpg


只解决一个问题,就是让所有线程同时执行。

https://img1.sycdn.imooc.com//5bd7b82200013ade05580194.jpg

https://img1.sycdn.imooc.com//5bd7b82b00014b9b05580315.jpg

https://img1.sycdn.imooc.com//5bd7b8360001441205580167.jpg


使用场景;

用于多线程计算数据,最后合并计算结果的计算场景。

https://img1.sycdn.imooc.com//5bd7b8550001de3e05580303.jpg

1.cpu评测软件。(所有线程同时开始)

 

2.零点零分双十一秒杀 (假设有20个奖品,设置20个线程去抽奖,在0点0分时必须同时跑,不能有先后顺序。)

 

3抢票软件。(在后车票开售时,就必须有300或500个线程同时跑进去抢票)


和countDownLatch区别:

countDownLatch:

1.计数器只能用一次。

2.实现一个或多个线程需要等待其他线程完成某项操作后,才能继续往下执行(描述的是一个或多个线程等待其他线程 关系)

CyclicBarrier :

1计数器可以重置(循环使用)

2.多个线程之间相互等待,知道所有线程都满足条件之后,才能继续执行后续操作(描述的是各个线程内部相互等待的关系,所以他能处理更复杂业务场景)。


juc之ReentrantLock重入锁(不太推荐使用)

https://img1.sycdn.imooc.com//5bd7b8de0001693905580221.jpg

比如我们拿到文件锁之后 跳出去做其他事,在回来还可以获得这个文件锁,就是可重入锁。

https://img1.sycdn.imooc.com//5bd7b9410001a05705580281.jpg

提高性能 是想尽办法 不让线程进入内核的阻塞状态。

 

总结  :

1.当只有少了竞争者时 synchronized (不会引发死锁,jvm 会自动解锁)是最佳选择

2.竞争者不少,但是线程增长的趋势是可以预估的这是推荐用ReentrantLock


juc 之Condition线程等待与唤醒

https://img1.sycdn.imooc.com//5bd7ba140001dc6205580280.jpg

任何需要预先设置好顺序的线程都需要Condition的支持

https://img1.sycdn.imooc.com//5bd7ba6900019a0505580233.jpg

https://img1.sycdn.imooc.com//5bd7ba7e0001da6b05580295.jpg

https://img1.sycdn.imooc.com//5bd7ba830001489805580252.jpg


juc之Callable&Future

https://img1.sycdn.imooc.com//5bd7baaf0001b0c905580283.jpg

https://img1.sycdn.imooc.com//5bd7bad80001fe1605580266.jpg

JUC-Fork/Join(jdk7 用于并行执行的框架) 框架

Fork/Join 是将大任务 分拆为若干个小任务,最终汇总每个小任务的结果后得到大任务结果的框架。fork---分拆,  join---合并。 

采用的是 工作窃取算法(如下图)

https://img1.sycdn.imooc.com//5bd7bb1c0001677205510389.jpg

假如需要做一个比较大的任务,可以把这个任务 分割个若干个互不依赖的小任务,为了减少线程间的竞争,把这些子任务分别放到不通的队列里,为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程处理A队列里的任务,但是有的线程会先把自己队列里的任务处理完,而其他线程队列里还有任务需要处理,于是先处理完的线程回去其他未完成的队列里窃取任务,而这时多个线程会访问同一队列,为了减少任务线程和被窃取任务的线程间的竞争,通常我们会使用双端队列,被窃取任务的线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾巴获取任务。

优点;充分利用线程进行并行计算,并减少了线程见得竞争

缺点: 在某些情况下还是存在竞争(比如双端队列里只有一个任务时),更加消耗系统资源。

局现性

1任务只能用fork和join来作为同步机制,如果使用了其他同步机制 工作线程就不能执行其他任务了。

2.所拆分的任务 不应该执行io操作(如读和写数据文件)

3.任务不能抛出检查异常,必须通过必要代码处理


代码:

类必须继承RecursiveTask<>类

其中Recursive是递归的意思,就是把大任务不断拆分成小任务。

下面例子做相加运算(从1加到100),假设相加任务很耗时,所以将任务分解多线程相加

流程,首先拆分子任务,之后让子任务各自执行,最后同过join 的方式合并结果

https://img1.sycdn.imooc.com//5bd7bd310001504905580253.jpg

https://img1.sycdn.imooc.com//5bd7bd4000019ae905580254.jpg

https://img1.sycdn.imooc.com//5bd7bd590001cd7a05580219.jpg

https://img1.sycdn.imooc.com//5bd7bd610001040205580221.jpg


JUC-BlockingQueue



线程安全,主要用于生产者和消费者模型

https://img1.sycdn.imooc.com//5bd7bd80000154d105580304.jpg

https://img1.sycdn.imooc.com//5bd7be130001068205580275.jpg

https://img1.sycdn.imooc.com//5bd7be1c00011c9704530158.jpg


线程池


https://img1.sycdn.imooc.com//5bd7be4700019b9b05580293.jpg

https://img1.sycdn.imooc.com//5bd7be4d0001ee4705580255.jpg


ThreadPoolExecutor

https://img1.sycdn.imooc.com//5bd7be5e0001d3e505580273.jpg

如果运行线程数小于corePoolSize,直接创建新线程处理任务,如果线程池中线程数量大于corePoolSize且小于maximumpoolSize则只有当workQueue满了的时候 才会创建线程到maximumpoolSize值,来处理任务

https://img1.sycdn.imooc.com//5bd7be9f0001ce5405580230.jpg

https://img1.sycdn.imooc.com//5bd7befd0001722805580287.jpg

https://img1.sycdn.imooc.com//5bd7bf6a00012a0a05260343.jpg


https://img1.sycdn.imooc.com//5bd7bfa70001ae3d05580336.jpg

https://img1.sycdn.imooc.com//5bd7bfac0001ce9005060182.jpg

https://img1.sycdn.imooc.com//5bd7bfb80001d74405580293.jpg


HashMap与ConcurrentHashMap解析

HashMap

底层结构是  数组+链表

HashMap 有两个参数影响性能是初始容量(16)和加载因子(0.75)

加载因子是能达到最大容量,如16*0.75=12 当放入12个元素后HashMap就需要扩容为原来的2倍

https://img1.sycdn.imooc.com//5bd7bfef0001d48704930223.jpg

HashMap 的寻址方式:

对于一个新插入或需要读取的数据,HashMap需要将其key按照一定计算规则计算出hash值并对数组长度进行取模,结果作为在查找中的index

单线程下rehash

https://img1.sycdn.imooc.com//5bd7c01a0001030505570233.jpg

多线程下rehash  (扩容) 容易出现死循环


ConcurrentHashMap(jdk7,基于分段锁处理的)

ConcurrentHashMap 底层结构任然是数组和链表,与HashMap不同的是最外层不是一个大数组而是一个Segment 数组

https://img1.sycdn.imooc.com//5bd7c04b0001553b05580343.jpg

与hashmap的不同点:

hashmap  线程不安全,如许key和value为空,不容许在遍历的时候修改

 

ConcurrentHashMap 线程安全 不如许key和value为空,如许遍历的时候修改,并且跟新对后面的遍历可见

java8下优化的ConcurrentHashMap  将底层数结构中的链表用了红黑树来提高并发性(默认并发数达到8时将链表变为红黑树)

https://img1.sycdn.imooc.com//5bd7c0650001652c05430327.jpg


https://img1.sycdn.imooc.com//5bd7c0850001cd3005580250.jpg


juc之Atomic与CAS算法(乐观锁)

https://img1.sycdn.imooc.com//5bd7c0b500016d5905580293.jpg

https://img1.sycdn.imooc.com//5bd7c102000122ce05580338.jpg

https://img1.sycdn.imooc.com//5bd7c1140001c6aa05580407.jpg

https://img1.sycdn.imooc.com//5bd7c1320001bb7e05580264.jpg


单例模式代码

/**
 * 双重同步锁单例模式
 * 限制程序  不让其发生指令重排(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;
         }
     }
 }


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消