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释放锁,不然容易造成死锁,可以判断锁的状态,是公平锁也是非公平锁,可以定义多个等待队列;
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)