多线程详解

MYSQL基础详解:https://www.cnblogs.com/itchang/p/5784863.html
事物隔离性详解:https://www.cnblogs.com/fjdingsd/p/5273008.html
阿里JAVA开发手册:http://kangroo.gitee.io/ajcg/#/naming-style
Java关键字final、static使用总结:http://blog.51cto.com/lavasoft/18771

--------------------------------------------------------------------------------------------------
多线程
进程:进程是线程的集合,正在运行的应用程序。
线程:线程是一条执行路径,一个独立的执行单元。
多线程作用:调高程序效率。
创建线程的方法:
    1:集成Thread类
    2:实现Runnable接口
    3:匿名内部类
    4:callable
    5:线程池
同步:代码从上往下执行,又称:单线程
异步:每个线程之间互不影响,又称:多线程
守护线程:和主线程,GC线程一起销毁
非守护线程:主线程销毁了,还是会继续执行的
进程中包括了,主线程,GC线程,用户线程(用户自己创建的线程,非守护线程)
多线程运行状态:新建状态  -  就绪状态  -  运行状态  -  阻塞状态  -  死亡状态
多线程运行状态流程图:https://img-blog.csdn.net/20161017181631639?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
线程安全问题:当多个线程共享同一个全局、静态变量,做写操作时,可能会受到其他线程的干扰,导致数据冲突的问题呢
解决线程安全问题:
    1:同步synchroized -- 自动释放 
    2:锁(locak)        -- 手动释放
为什么使用同步或者锁能解决线程安全问题?
    多个线程共享同一个全局、静态变量时,只让一个线程的代码执行,代码执行完毕后释放锁,在让其他线程执行。
这里需要注意一下,
同步和线程同步不是一个概念
程序中的同步是单线程的,代码从上往下执行
线程同步是保证线程安全
同步synchroized执行方法
1:同步代码块  -- 使用的是对象锁
    synchroized(锁){}
2:同步函数        -- 使用的是this锁
    public synchroized void add(){}
3: 静态同步函数        -- 使用的是当前字节码文件锁(类名.class)
    public static synchroized void add(){}

注意:在多线程的同步中使用的不是同一把锁,那么线程还是不安全的

同步synchroized必须有的条件
    1:必须要有两个线程以上,需要发生同步
    2:多个线程想同步,必须用同一把锁
    3:保证只有一个线程进行执行

同步synchroized原理
    1:有一个线程已经拿到锁了,其他线程会一直等待,直到线程释放了锁,其他线程将会抢这把锁,抢到锁的线程将会执行代码。
同步synchroized缺点:效率低
死锁:同步中嵌套同步,无法释放,一直等待,变成死锁。

多线程三大特性
原子性:在操作中要么都执行,要么都不执行,保证数据一致
可见性:java内存模型,一个线程修改了共享变量值,其他线程能过立即得知这个修改后的值
有序性:程序执行的顺序按照代码的先后顺序执行

java内存模型(JMM):一个线程对共享变量写入是,能对别的线程可见
流程图:https://sfault-image.b0.upaiyun.com/140/667/1406676357-594a0dc7e13e8_articlex
java内存模式
主内存:主要存放共享全局变量
私有本地内存(工作内存):本地线程私有变量

看流程图就知道为什么数据会不可见了。
线程1从主内存中获取到共享变量(A)到工作内存1中,然后在修改共享变量(A)的值,在修改期间,线程2从主内存中获取到共享变量(A)到工作内存2中(这里需要注意的是,线程1还没修改值,线程2获取到的是旧的值),线程1修改完后将共享变量(A)传递给主内存中,这就发生了可见行的问题。
加上volatile就可以解决可见性问题:private static volatile int A;

volatile的作用:保证线程之间可见性,不保存原子性
AtomicInteger的作用:保证原子性
多线程通讯方式:https://static.oschina.net/uploads/space/2018/0125/120221_Ronq_3578766.png


wait():让当前线程从运行状态变成休眠状态,释放锁的资源
notify(): 让单钱线程从休眠的状态变成运行状态
注意,在同步中才能使用wait()、notify()
join():等待主线程执行完毕
Lock锁
Lock锁的写法
    Lock lock  = new ReentrantLock();
    lock.lock();
    try{
    //可能会出现线程安全的操作
    }finally{
    //一定在finally中释放锁
    //也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常
      lock.ublock();
    }

Lock锁与synchronized同步锁的区别
lock手动锁
synchronized同步自动锁 

Condition用法
 Condition的功能类似于在传统的线程技术中的,Object.wait()和Object.notify()的功能。


线程安全的类
Vector和ArrayList区别
    实现原理都是通过数据实现,查询速度快,增加,修改,删除速度慢
    区别:线程安全问题
    Vector是安全(上锁的集合),ArrayList线程不安全
    ArrayList效率高
    
Hashtable和HasMap集合
    Hashtable线程是安全
    HasMap线程是不安全
    链表+数组 put Hascode取模得到下标位置 一致性取模算法
    证明:Hashtable是线程安全?查看源码的put方法

JDK并发包
前言:
    在JDK1.5之后,发明一种新并发包
    JDK1.5之后,产生了很多java并发包
    Hashtable线程是安全,效率非常低,锁的资源竞争。
    多线程共享同一个hashtable 加锁、影响效率、每次只能有个线程去操作hashtable

并发安全类
ConcurrenHashMap -- 并发包
    分段锁 默认16段
    将一个整体(集合)拆分成多个小的Hashtable,默认分成16段
    这样就可以有效的减少锁的资源竞争
    
CountDownLatch --计数用的

CyclicBarrier --计数用的

并发队列 --生产者消费者概念
在并发队列中分有界、无界
有界、无界的区别
    为防止理解不深,先引用一个问题
        数组和集合的区别
            数组是有长度限制的、集合是没有长度限制的
    同等
    有界是有限制的,无界是无限制的

阻塞队列、非阻塞队列 --这里要注意是阻塞'队列'
区别:
    生产者写入满的时候,就会进入到阻塞。
    当队列为空的时候,消费者也会进入阻塞。
    
队列遵循的规则
    先进先出
    后进后出

ConcurrentLinkedDeque和BlockingQueue的区别:
    BlockingQueue可以阻塞,有界。
    ConcurrentLinkedDeque不可阻塞,无界。


多线程
什么是线程池
    Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序
    都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。
    第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
    第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,
    还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用
    线程池,必须对其实现原理了如指掌。

线程池作用
    线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。
    如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。

在线程池中 ThreadPoolExecutor 是核心类

线程池四种创建方式
Executor封装了四种线程池类型
Java通过Executors(jdk1.5并发包)提供四种线程池,分别为:
    1,newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    2,newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    3,newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
    4,newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。


java中线程池 核心使用的构造函数 采用ThreadPoolExecutor
ThreadPoolExecutor构造函数参数
    corePoolSize: 核心池的大小
    maximumPoolSize: 线程池最大线程数
    keepAliveTime: 终止时间
    unit: 超时秒数

线程池原理
线程池原理剖析
提交一个任务到线程池中,线程池的处理流程如下:
1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
2、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

线程池 合理配置 --
    CPU密集 : 线程数和CPU数相同
        CPU使用频繁就使用
    IO密集:2*CPU核数
        操作数据库,IO需要等待,线程都需要等待,阻塞
        
CPU密集和IO密集的区别
    IO密集经常会有阻塞,休眠,CPU密集会频繁的调用


java锁机制
    悲观锁
    乐观锁
    分段锁
    重入锁
    读写锁
    CAS无锁
    自旋锁
    排它锁

    
案例:
    买水果案例
        小明买了10块水果,给了老板钱。然而小红不知道小明给了钱,小红又给了老板10块。(数据重复)
    在“多数据源”的情况下(2个或者2个以上的JDBC连接)就要使用锁
    
什么是悲观锁?
    默认加排它锁
    每次在拿数据的时候,都会上锁。
    悲观锁:悲观锁悲观的认为每一次操作都会造成更新丢失问题,在每次查询时加上排他锁。
    每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿    这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如    行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

    使用方法:
        SQL语句后面添加 for update
    例子:
        select * from order for update
    原理:
        只允许有一个连接进行操作,当一个线程进入后会锁住,别的线程要等待锁释放才能使用
    缺点:
        效率低 --因为只能保证一个连接进行操作
    
什么是乐观锁?
    版本控制
    乐观锁:乐观锁会乐观的认为每次查询都不会造成更新丢失,利用版本字段控制
    原理:
        首先数据库中有个版本号(version),2个数据源同时操作SQL语句,第一个操作的时候version是0,然后会把version+1(版本号+1),那么第二个操作的时候查询version=0的数据是没有的,查不到的,最后判断影响行数就可以了,假如影响行数>0就可以进行操作

    悲观锁和乐观锁的区别
        如果查询量小,可以使用悲观锁
        使用版本控制操作,使用乐观锁


什么是重入锁?
    重入锁(ReentrantLock 和synchronized) 与 非重入锁(死锁) 递归使用同步


什么是读写锁?
    两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写(译者注:也就是说:读-读能共存,读-写不能共存,写-写不能共存)
    

什么是CAS无锁?
    原子类底层如何实现保证线程安全,CAS无所机制效率比有所机制搞。
    CAS无所机制其实和乐观锁概念类似
    
    CAS体系中有三个参数
        它包含三个参数CAS(V,E,N): V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。
    实现过程:
        当V=E表示没有任何线程操作,那么N值改为V值,返回N
        但V!=E表示有线程操作过,那么N值改为E值,返回V
    例子:
        AtomicInteger就是CAS无锁机制实现的


什么是自旋锁?
    内部不停的进行循环

转载于:https://my.oschina.net/u/3578766/blog/1613077

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值