多线程复习总结

多线程复习总结,第1张

1.主线程可以捕获到子线程的异常吗?

        线程设计理念:“线程的问题应该由线程本身解决,而不应该委托到外部”

        

        如果想要捕获到子线程的异常可以使用Thread的静态方法:

         Thread.setDefaultUncaughtExceptionHandle

        (new MyUncaughtExceptionHandle());

2.线程池的七个参数

        corePoolSize:线程池核心线程大小;

        maximumPoolSize:线程池最大线程数;

        keepAliveTime:空闲线程存活时间;

        unit:空闲线程存活时间单位;

        workQueue:线程阻塞队列;

        threadFactory:线程工厂;

        handle:拒绝策略;

                ①:CallerRunsPolicy:直接抛弃以前任务,执行当前任务;

                ②:AbortPolicy:直接丢弃任务,并抛出异常;

                ③:DiscardPolicy:直接丢弃任务,不抛出异常;

                ④:DiscardOldestPolicy:直接抛弃最早进入阻塞队列的任务,然后尝试把当前任务放入阻塞队列的最后一个位置,不是取代之前线程的任务!!!

3.线程池的执行流程

        假设来了一个新的任务,

                ①:先判断核心线程数有没有满,没满则直接创建线程执行任务,满了进行第二步;

                ②:判断阻塞队列有没有满,没满放入阻塞队列,满了执行第三步;

                ③:判断空闲线程数有没有满,没满创建空闲线程,满了执行第四步;

                ④:执行拒绝策略,四种策略方式上面提了;

4.线程池四种创建线程的方式

       

=====================================================================

=====================================================================

=====================================================================

 

5.线程的创建方式

        继承Thread类;

        

        实现Runnable接口;

        

        实现Callable接口;

        

        线程池;

6.线程的状态

        新建状态:新创建一个对象,但还没调用start方法;

        

        可运行状态:调用了start方法,但不代表一定抢到了CPU时间片,只是处于在抢CPU的状态;

        

        运行状态:线程抢到了CPU时间片,在执行run()方法;

        

        阻塞状态:wait()放锁,sleep()不放锁;

        

        死亡状态:线程执行结束;

7.什么是线程安全

        多线线程调用一个共享资源时,都能得到自己想要的结果,结果不被别的线程所污染,这就是线程安全;

8.什么是并发和并行

        并发:一个CPU对多个线程或进行之间进行多路复用,即CPU执行多个任务,轮流执行;

        

        并行:多个线程或进程在同一时刻被执行,必须要有多个CPU支持;

9.共享资源、临界资源、临界区、互斥

        共享资源:多个线程都会访问的资源;

        

        临界资源:一次仅允许一个进程使用的资源,各进程采取互斥的方式,实现的共享资源称之为临界资源;

        

        临界区:每个进程访问临界资源的那段代码称之为临界区,每次只允许一个线程进入临界区;

        

        互斥:当多个线程访问同一资源时,要求在同一时间只允许一个线程 *** 作共享资源, *** 作完毕后别的线程才可以进行 *** 作,具有唯一性和排他性;

                互斥通过synchronized关键字即加锁的方式实现;

10.锁

同步锁:    同步方法    同步对象
            同步锁也叫对象锁,用于保证线程安全的,是阻塞式的解决方案;
            synchronize可以保证线程方法和代码块在运行时,同一时刻只有一个方法可以进入临界区,同时还可以保证共享变量的内存可见性;
    
同步锁原理:
            java中每一个对象都可以作为锁,这是synchronize实现同步的基础;
            普通方法同步:锁是当前实例对象;
            静态方法同步:锁是当前类的class对象;
            同步方法块:锁是括号里面的对象;
    
Monitor:
            java平台中,每一个对象都有一个Monitor与之关联,Monitor又称为内部锁,监视锁;
            java的同步机制、互斥锁机制,保证了在同一时刻只能有一个线程可以访问共享资源,这些机制的保障来源于监视锁Monitor;
    
Monitor原理分析:
            当多个线程同时访问一段同步代码时,首先会进入EntryList队列,当某个线程获得对象的Monitor后进入Owner区域并把monitor中的owner变量设置为当前线程,同时monitor中的计数器count+1。即获得锁;

        JVM对于同步方法和同步代码块的处理方式不同:
    对于同步方法:JVM采用ACC_SYNCHRONIZED标记符来实现同步;
        方法级的同步是隐式的。同步方法的常量池中会有一个ACC_SYNCHRONIZED标志。当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED,如有        设置,则需要先获得监视器锁,然后开始执行方法,方法执行之后再释放监视器锁。这时如果其他线程来请求执行方法,会因为无法获得监视器锁而被阻断住。值得        注意的是,如果在方法执行过程中,发生了异常,并且方法内部并没有处理该异常,那么在异常被抛到方法外面之前监视器锁会被自动释放。
    对于同步代码块:JVM采用monitorenter、monitorexit两个指令来实现同步;
    monitorenter:
        每一个对象都有一个monitor,一个monitor只能被一个线程拥有。当一个线程执行到monitorenter指令时会尝试获取相应对象的monitor,获取规则如下:如         果monitor的进入数为0,则该线程可以进入monitor,并将monitor进入数设置为1,该线程即为monitor的拥有者。如果当前线程已经拥有该monitor,只是            重新进入,则进入monitor的进入数加1,所以synchronized关键字实现的锁是可重入的锁。如果monitor已被其他线程拥有,则当前线程进入阻塞状态,直到          monitor的进入数为0,再重新尝试获取monitor。
    monitorexit:
        只有拥有相应对象的monitor的线程才能执行monitorexit指令。每执行一次该指令monitor进入数减1,当进入数为0时当前线程释放monitor,此时其他阻塞         的线程将可以尝试获取该monitor,每个对象自身维护这一个被加锁次数的计数器,当计数器数字为0时表示可以被任意线程获得锁。当计数器不为0时,只有获得锁         的线程才能再次获得锁。
    
乐观锁-悲观锁:
    乐观锁是一种乐观思想,认为读多写少,遇到并发写的可能性低,每次去拿数据都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下再次期间别人有没有去更新这个数据,采取在写的时候先读出当前的版本号Version,然后加锁 *** 作(比较跟上一次的版本号,如果一样则更新),如果失败则重复读-比较-写的 *** 作;java中的乐观锁基本都是基于CAS *** 作实现的,CAS是一种更新的原子 *** 作,比较当前值和传入值是否一样,一样则更新,否则失败!!!
    
    悲观锁是一种悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的都会上锁,这样别人想读写这个数据就会block直到拿到锁;Java中的悲观锁就是Synchronized,AQS框架下的锁则是先cas乐观锁去获取锁,获取不到才会转换为悲观锁;
    
    
自旋锁:
    当一个线程去获取一把锁的时候,该锁已经被别的线程持有,那么此线程将会等待,间隔一段时间会再次尝试获取;(自旋锁不建议长时间持有,消耗Cpu资源!!!)
    
    
独享锁:
    一个锁一次只能被一个线程所持有;
    
共享锁:
    一个锁可以被多个线程所拥有;
    
偏向锁:
    当一个线程访问对象并获取锁的时候,会在对象头里面存储偏向这个线程的ID,以后该线程在访问该对象的时候只需要对mark word的头标记进行判断,如果有就不需要        进行CAS *** 作;
轻量级锁:
    当竞争激烈时,偏向锁就会升级成轻量级锁;竞争激烈==多个线程争一把锁!!!
重量级锁:
    就是synchronized,得不到锁的线程会被阻塞,这就是重量级锁;
    
锁升级过程:
    当线程访问一个对象时,会检测mark word里面是不是当前线程ID,如果是则表示当前线程处于偏向锁,如果不是,那么会使用CAS将当前线程的ID替换mark word里面的标记,如果成功则带代表当前线程处于偏向锁,如果失败则说明发生竞争,撤销偏向锁,升级为轻量级锁,当前线程使用CAS将对象头的Mark Word替换为锁记录指针,如果成功,则当前线程获得锁,如果失败,表示有其他线程竞争锁,当前线程便尝试使用自旋来获取锁,如果自旋成功,则依然处于轻量级,如果自旋失败,则升级为重量级锁(Synchronized);

    
非公平锁:
    获取锁的时候不是根据所申请的时间的先后给等待的线程分配锁的,谁抢到谁执行;
    
公平锁:
    每个线程抢占锁的顺序为先后调用lock方法的顺序依次获得锁,先调先执行;
    
死锁:
    产生的直接原因是占用共享资源的线程正在请求其他的共享资源,而该资源正在被其他的线程占用,且占用该资源的线程正在请求其他共享资源,从而导致了死锁,导致线程永远无法继续运行;
    
形成死锁的必要条件:
    互斥条件:一个资源每次只能被一个进程使用;
    不可抢占条件:一个线程已经占有的资源,未经本线程释放的情况下,其他线程不能剥夺;
    占有且申请条件:线程投入时,不是一次性申请所需资源,而是运行中按需要临时动态的申请;
    循环等待条件:系统中几个系统形成循环地等待对方所占用资源的关系;
    
如何避免死锁:
    打破四个必要条件之一;

11.如何给对象加锁

        放在java的对象头Mark Word,MarkWord的长度是32-64位的虚拟机,最后两个字节位是锁状态的标志位,用来标记锁当前的状态,对象所处的状态,决定了Markword存储的内容;

12.什么是AQS

        队列同步器:用来构建锁或者其他同步组件的基础框架,通过内置的先来先服务队列完成资源获取线程的排队工作,AQS是实现锁的基础;

13.线程通信

        两种方式:
                 管道流:
                        Java中提供了两个 pipedInputStream 和 pipedOutStream 处理管道流的类;
                wait/notify机制:
                        wait():执行此方法,当前线程进入阻塞状态,并释放同步监视器;
                        notify():唤醒一个被wait的线程,如果有多个线程,就唤醒优先级最高的线程;
                         notifyall():会唤醒所有被wait的线程;
        使用前提:这三个方法均只能使用在同步代码块或者同步方法中。且必须获得对象的锁才能调用,使用锁对象去调用,否则会抛异常!!!

14.sleep和wait的区别

        相同点:
                都会使线程进入阻塞状态
        不同点:
                声明的位置不同,Thread类中声明sleep,Object类中声明wait;
                调用的要求不同,sleep可以在任何需要的场景下调用,wait必须使用在同步代码块和同步方法中;

15.线程同步

        同步的实现方式:
                同步方法:Java中的每个对象都有一个内置锁,当使用此关键字修饰方法时,内置锁会保护整个方法,在调用该方法前,需要获得内置锁,否则就处于阻塞状态;
                同步代码块:即有synchronized关键字修饰的语句块。 被该关键字修饰的语句块会自动被加上内置锁,从而实现同步;
                wait与notify:详细见:wait、notify、notifyAll的使用方法;
                重入锁实现线程同步:详见Lock接口,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力;
                使用特殊域变量(volatile)实现线程同步:volatile关键字为域变量的访问提供了一种免锁机制,被volatile修饰的遍历,编译器会直接从内存访问!!!
                使用局部变量ThreadLocal实现线程同步:ThreadLocal的作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性;

16.关闭线程池

        当线程池中的任务都处理执行完毕后,线程不会自动关闭,可以调用shutdown方法和shutdownNow方法来关闭线程池;
         两者区别在于:
                shutdown:不允许新的任务提交,会等待线程池中的所有任务执行完毕后关闭线程池;
                 shutdownNow:不允许新的任务提交,直接关闭线程池中所有正在运行的任务并关闭线程池;

17.synchronized和Lock的区别

        synchronized:是一个关键字,存在于JVM层面,获取锁的线程执行完同步代码会释放锁,如果发生异常,JVM会让线程释放锁,不会造成死锁,无法判断锁的状态,是非公平锁,是独享锁,只有一个等待队列;
        Lock :是一个接口,不会自动释放锁,需要在finally中必须使用unlock释放锁,不然容易造成死锁,可以判断锁的状态,是公平锁也是非公平锁,可以定义多个等待队列;

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/796269.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存