JavaSE多线程

锁的基础知识:

锁的类型:

悲观锁和乐观锁

乐观锁:认为读多写少,遇到并发写的可能性极低,即每次去拿数据的时候认为别人不会修改,所以不会上锁,但是在更新的时候会判断在此期间有没有                人更新这个数据。

              判断依据:在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样就更新),如果失败就重复 读 ->  比较 ->写的操作

             Java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败

CAS:

        简单的来说,CAS有三个操作数,内存值 V, 旧的预期值 A,要修改的新值B。当且仅当预期值A与内存值V相等时,将内存值修改为B,否则返回V。

        这是一种乐观锁的思路。它相信在它之前没有线程去修改内存值

        缺点:会发生 ABA问题,即A被修改成B,然后又被修改成A,不能感知到修改

悲观锁:认为写多读少,遇到并发写的可能性高,每次在读写数据的时候都会上锁,如果别的线程想读写这个数据就会block直到拿到锁

1.Synchronized底层实现(*****):

Java对象头:锁的对象保存在对象头中,synchronized锁的是对象

锁的状态分为:自旋锁,偏向锁,轻量级锁,重量级锁)

自旋锁:加锁后,只有一个线程进入代码块,其他线程等待(自旋等待),为重量级锁(monitorenter,重量级锁标志)

自旋锁:1.相当于怠速停车,具有不公平性,处于自旋状态的锁比重量级锁(它处于阻塞状态)更容易获得锁

              2.自旋锁不会引起调用者立即睡眠,如果自旋锁已经被别的执行单元保持,调用者不放弃处理器的执行时间,进行忙循环(自旋),

                 跑的是无用的线程,JDK1.6后默认开启了自旋锁,自旋次数默认10次

              3.自旋锁一直占用CPU,在未获得锁的情况下,一直进行运行 - - - 自旋,若不能在很短的时间内获得锁,将会使CPU效率降低

自适应自旋:1.减少无用线程占用CPU的问题

                     2.自旋的时间不再是固定的,由前一个在同一个锁上的自旋时间及锁拥有者的状态来决定。

                     3.如果在同一个锁对象上,自选等待刚好成功获得锁,并且持有锁的线程正在运行中,那么虚拟机就认为这次自旋很有可能会获得锁,

                        将会允许自旋等待更长的时间

重量级锁(Java头中的monitorenter对象):os需要从用户态 -> 内核态,开销较大。同一时刻多个线程竞争资源

轻量级锁(CAS操作):多线程在不同时刻访问共享资源,乐观锁的一种

偏向锁:更为乐观的锁,假定从始至终都是同一个线程在访问共享资源

 

JDK中锁只有升级过程,没有降级过程。  无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁

 

锁消除:消除类似于Vector等线程安全集合不存在共享资源竞争时,JVM将代码优化,解锁

2.synchronized 与 ReentrantLock 区别

synchronized:

synchronized是Java中最基本同步互斥的手段,可以修饰代码块,方法,类

在修饰代码块的时候需要一个reference对象作为锁的对象

在修饰方法的时候默认当前对象作为锁的对象

修饰类的时候默认当前类的class对象作为锁的对象

synchronized会在进入同步块的前后分别形成monitorenter和monitorexit字节码指令,在执行monitorenter指令时会尝试获取对象的锁,如果

 此对象没有被锁,或此对象已被当前线程锁住,则锁的计数器+1,如果monitorexit被锁的对象的计数器-1,直到为0就释放该对象的锁,由此

synchronized是可重入的,不会将自己锁死。

ReentrantLock:

除了synchronized的功能,多了三个高级功能

1.等待可中断  2.公平锁  3.绑定多个Condition

1.等待可中断

    在持有锁的线程长时间不释放锁的时候,等待的线程可以放弃等待. tryLock(long timeout,TimeUnit unit)

2.公平锁

    按照申请锁的顺序来一次获得锁称为公平锁,synchronized的是非公平锁,ReentrantLock可以通过构造函数实现公平锁

     new ReentrantLock(boolean fair)

3.绑定多个Condition

    通过多次newCondition可以获得多个Condition对象,可以简单的实现比较复杂的线程同步的功能

总的来说,lock更加灵活

二者相同点: Lock能完成synchronized所实现的所有功能

3.Lock(AQS: AbstractQueuedSynchronized 核心概念:一个是表示(锁)状态的变量、一个是队列)

4.Lock与Synchronized的区别

   a.synchronized内置关键字,在JVM层面实现,发生异常时,会自动释放线程占有的锁,因此不会发生死锁现象。

     Lock在发生异常时,如果没有主动通过unLock去释放锁,很可能产生死锁现象,因此使用Lock时需要在finally块中释放锁

  b. Lock具有高级特性: 时间锁等候,可中断锁等候

  c.当竞争资源非常激烈时(即有大量线程同时竞争时),此时Lock的性能要远远优于synchronized

5.线程池

JDK内置的四大线程池:

 1.创建无大小限制的线程池:

      public static ExecutorService newCachedThreadPool()

2.创建固定大小的线程池:

     public static ExecutorService newFixedThreadPool(int nThreads)

3.单线程池

     public static ExecutorService newSingleThreadExecutor()

4.创建调度线程池

     public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

线程池的三大优点

   a.降低资源消耗:通过重复利用已创建的线程降低线程创建与销毁带来的消耗

   b.提高响应速度:当任务到达时,不需要等待线程创建就可以立即执行

   c.提高线程创建的可管理性:使用线程池可以统一进行线程分配、调度和监控

线程池的组成

1. corePool:核心线程池

2. BlockingQueue: 阻塞队列

3. MaxPool: 线程池容纳的最大线程容量

a. 如果当前执行的线程数 < corePoolSize ,则创建新的线程执行任务,然后将其放入corePool(需要全局锁)

b. 如果当前线程数 >= corePoolSize,将任务放入阻塞队列等待调度执行 (95%)

c. 如果阻塞队列已满,则试图创建新的线程来执行任务(需要全局锁)

d. 如果创建线程后,总线程数 > maxPoolSize,则任务被拒绝,调用拒绝策略返回给用户

工作线程

               线程池创建线程时,将线程封装为worker,worker在执行完任务后,还会循环从工作队列中取得任务来执行

手工创建线程池的六个参数

ThreadPoolExecutor tye = new ThreadPoolExecutor(

     int corePoolSize,

     int maximumPoolSize,

     long keepAliveTime,

     TimeUnit unit,

     BlockingQueue<Runnable> workQueue,

     RejectedExecutionHandler handler

)

1. corePoolSize(核心线程池): 当提交一个任务到线程池时,线程池会创建一个新的线程执行这个任务,即使其他基本线程也可以执行这个任务也会创建                                                       新的线程,直到当前线程池中的线程数量大于基本大小

2. BlockingQueue(阻塞队列):  用于保存等待执行任务的阻塞队列

     a. ArrayBlockingQueue: 基于数组的有界阻塞队列。按照FIFO对元素排序

     b. LinkedBlockingQueue: 基于链表的无界阻塞队列,吞吐量高于 ArrayBlockingQueue

                                              FixedThreadPool(),SingleThreadPool() 都用此队列

     c. SynchronousQueue: 不存储元素的阻塞队列,每个插入操作必须等待另一个线程移除操作,否则插入操作一直处于阻塞状态

                                           吞吐量 > LinkedBlockingQueue

                                           cachedThreadPool()采用此队列

     d. PriorityBlockingQueue: 具有优先级的无界阻塞队列

3.keepAliveTime(线程活动保持时间): 线程的工作线程空闲后,保持存活的时间

4.TimeUnit : KeepAliveTime 的时间单位

5.RejectedExecutionHandler(饱和策略)(拒绝策略):线程池满时无法处理新任务的执行策略

线程池默认采用 AbortPolicy (抛出异常)-----可以省略此参数

6.死锁的产生原因(四大条件)以及处理方案(银行家算法)

     a. 互斥条件:进程对所分配的资源进行排他性使用,即一段时间内,某资源只能由一个进程占用,如果此时还有其他进程请求该资源,则请求者只能等                               待     

     b. 请求和保持条件:即进程已经保持至少保持一个资源,但是还在请求别的资源,但此时别的资源被其他线程持有,此时请求进程阻塞,此进程也不                                         想放弃所持有的资源

     c. 不剥夺条件:进程已经获得的资源,不能被剥夺,只能由进程使用完自己释放

     d. 环路等待条件:在发生死锁时,必然存在一个竞争资源的环形链。即进程集合中 {p0,p1,p2,...,pn},p0等待p1的资源,pn等待p0的资源

银行家算法

                 是用来避免操作系统出现死锁的有效算法

7. volatile 两层语义 - 懒汉式单例为何使用双重加锁

   a.禁止指令重排

   b.保证内存可见性

// 懒汉式单例模式
// 声明 : 代码中出现的问题前提是 第二行代码未被 volatile修饰

public class Singleton{                             // 1
    private static volatile Singleton singleton;    // 2
    
    private Singleton(){}
    
    public static Singleton getInstance(){          // 3
        // 双重检查
        if(singleton==null){                        // 4 第一次检查
            synchronized(Singleton.class){          // 5 加锁
                if(singleton==null){                // 6 第二次检查
                    return singleton = new Singleton();  // 7 问题在这里
                }  
            }
            
        }
        return singleton;    

    }
        

}

 在多线程情况下,上面第七行代码 singleton = new Singleton(); 创建一个对象分为三步

   

memory = allocate(); // 1:分配对象的内存空间

ctorInstance(memory); // 2:初始化对象

singleton = memory; // 3.设置 singleton 指向 刚分配的内存地址


上面三行代码可能会被重排序:

memory = allocate(); // 1:分配对象的内存空间

singleton = memory; // 3.设置 singleton 指向 刚分配的内存地址

ctorInstance(memory); // 2:初始化对象

这里由一个问题就是,在还没有初始化对象的时候就将singleton指向了内存地址

8.NIO(Netty)

NIO如何实现多路复用,BIO,NIO,AIO特点

  多路复用:在Java 1.4中引入了NIO框架(Java.nio包),提供了Channel,Selector,Buffer等新的抽象类,可以构建多路复用的,同步非阻塞IO程序,同时提供了更接近操作系统底层的高性能数据操作方式

  AIO:在JDK1.7中,NIO有了新一步改进,既 NIO2 ,引入了异步非阻塞IO方式,也叫AIO,

                   异步IO操作基于事件和回调机制,可以简单理解为,应用操作直接返回,不会阻塞在那里,当后台处理完成,操作系统会通知相应线程进行后                     续工作 

 BIO,NIO,AIO 特点:

   BIO:该方式适用于数目比较小且固定的架构,这种方式对于服务器资源要求比较高,并发局限于应用中,是JDK1.4以前唯一的选择,但程序直观简单易理解

   NIO: 该方式适用于数目比较多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4以后支持

   AIO: 适用于数目比较多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参加并发操作,编程比较复杂,JDK1.7开始支持

 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值