欢迎使用CSDN-markdown编辑器

来源:互联网 发布:淘宝运费模板设置 编辑:程序博客网 时间:2024/06/05 16:38
• 线程安全什么是线程安全?    当多个线程访问某一个类(对象或方法)时,这个类能表现出正确的行为,那么这个类(对象或方法)就是线程安全的synchronized:就是给对象或方法加锁,而加锁的代码称为“互斥区”或“临界区”示例总结:![](http://www.wxcha.com/biaoqing/16401.html)    当多个线程访问同一个加锁方法时,是以线程排队的形式访问(这个排队的顺序是CPU指定的,不是代码指定的)    当一个线程想要执行synchronized方法时,必须要拿到锁才能执行,拿不到锁,这个线程会一直尝试去拿锁,直到拿到为止;    而且多个线程同时去竞争这把锁(也就会有锁竞争问题)锁竞争:当有1000个线程,去同时拿一把锁,会导致CPU急速增高,会导致系统挂掉,尽量避免锁竞争问题如何避免锁竞争?但作为一套与锁完全无关的线程解决方案,在高并发或竞争激烈的场景,使用ThreadLocal可以在一定程度上减少锁竞争线程三要素:1、CPU:由OS负责调度   2、Data:堆空间共享,栈空间独立   3、code• 实现线程安全的三种方法?①  阻塞同步(互斥同步):加锁,悲观,synchronized、Lock②  非阻塞同步:CAS,乐观,Atomic③  不同步:ThreadLocal• 多个线程多个锁多个线程多个锁:多个线程,每个线程都能拿到指定的锁,分别获得锁之后,执行synchronized方法体的内容示例总结:    synchronized所修饰的是对象锁,每个线程对象都有自己的锁,互不影响(对象级别锁);    如果是同一线程对象调用对象内不同的同步方法,就需要等待,等第一个方法执行完释放锁之后,第二个方法才能执行;    在synchronized之前加static那就是类锁,多个线程对象都是同一把锁(类级别锁);• 对象锁的同步和异步同步:synchronized,同步的概念就是资源共享,如果不是共享资源,就没有必要进行同步,也就没有必要进行加锁;多线程访问同一个方法时,会排队访问异步:asynchronized,异步的概念就是独立,比如浏览页面时,可以点击按钮发送ajax请求;多线程访问同一个方法时,会并发访问同步的目的:是为了线程安全线程安全需要满足两个特性:原子性(同步)、可见性• 脏读什么是脏读?就是数据的不一致性示例总结:setValue加synchronized,getValue没加synchronized,a线程设值,b线程取值,至此出现数据不一致性,要同时加synchronized• synchronized锁重入synchronized有锁重入的功能,就是当一个线程对象进入此对象的加锁方法后,在此方法内可以再次进入此对象的其他加锁方法(父子继承关系也可以)• 出现异常锁会自动释放当加锁方法出现异常,锁会自动释放,如果不及时处理,此时其他线程会进入加锁方法,会导致严重错误解决方法:1-当出现异常时,结束此线程,throw new RuntimeException           2-当出现异常时,打断异常,catch (InterruptedException e)           3-当出现异常时,如果是在循环体中,可以continue,执行下一次当所有的任务是一个整体的时候,应该用1和2方法解决;如果所有任务不是一个整体,不相互影响,可以使用第3个方法解决。• synchronized代码块比如a线程执行一个很长时间的加锁方法,b线程下就必须要等待很长时间;这时就可以利用synchronized代码块,来优化代码执行的时间。不要用String常量当做锁对象,会出现死循环,可用new String("")的形式;另一种情况是,不要在方法内修改String常量值(如果是一个Person对象,修改其中的age属性,是可以的)• 解决指令重排序,而导致程序输出错误结果1> 就是加锁,加锁后代码就是单线程了,单线程下的指令重排序是不会导致结果错误的(因为as-if-serial语义,所以单线程不会出现错误结果)2> volatile,禁止指令重排序3> final,不可修改变量为什么会有指令重排序问题?是因为 编译器和CPU为了代码的执行效率,可能会进行指令重排序• volatile关键字作用:修饰变量关键字(就是说变量在多个线程间实时共享)作用:使变量在多个线程可见,但是没有同步功能,修饰变量关键字,禁止了指令重排序没有volatile之前,使用synchronized加锁来实现,变量一致性那synchronized和volatile区别?volatile是实时共享,不造成阻塞synchronized需要先获取锁,造成阻塞volatile只能用于多线程变量可见操作,而不能代替synchronized的同步功能(原子性)volatile只能应用于变量级别,而synchronized修饰方法或者代码块、volatile是轻量级的synchronized,性能要比synchronized强很多,不会造成阻塞;Atomic类系列对象支持原子性,注意:Atomic类系列对象只支持一次方法的原子性操作,不能支持多次方法的原子性操作synchronized(效率低) = volatile + Atomic类系列对象(效率高)在java(JDK1.5以后)中,每个线程都有自己的工作内存区,其中存放着,共享内存(所有线程共享的)中变量值得拷贝;也就是当你不加volatile关键字时,只会操作自己工作内存的变量,加了volatile之后,会强制线程去主内存中读取当线程执行时,他在自己的内存中操作这些拷贝的变量;一个线程如何存取一个共享变量?    一个线程通常先去获取锁,再去清除自己的内存工作区,把这些共享变量,从共享内存中正确的装入到自己的内存工作区中,当线程解锁时,将值写到共享内存一个线程可以执行的操作:使用(use)赋值(assign)装载(load)存储(store)锁定(lock)解锁(unlock)主内存(共享内存)可以执行的操作:读(read)写(write)锁定(lock)解锁(unlock)每个操作都是原子操作volatile作用?    强制线程去主内存(共享内存)中读取变量数据,而不去自己的内存读取,从而实现多线程变量可见,也就满足了线程安全的可见性还有一种使线程间变量共享的方案,就是给这个变量加锁,但是这种方式效率低。volatile只具备可见性,不具备原子性;AtomicInteger具备原子性,Atomic……类• 线程间通信什么是线程通信?    线程是操作系统 独立的个体,不能成为一个整体,而线程间通信就是就是成为整体的方式之一    wait和notify实现线程通信,必须配合synchronized 如何实现实时通信?    但是notify没有实现实时通知;    此时需要用到CountDownLatch类,countDownLatch.countDown(通知)、countDownLatch.await(等待);    CountDownLatch类不需要配合synchronized • ThreadLocal线程局部变量,是一种多线程间,并发访问变量的解决方案;与synchronized 加锁的方式不同,ThreadLocal完全不提供锁,而使用空间换时间的方式,为每个线程提供变量的独立副本,以保障线程安全。    从性能上说,ThreadLocal不具有绝对优势;在并发不是很高的时候,加锁的性能会更好;但作为一套与锁完全无关的线程解决方案,在高并发或竞争激烈的场景,使用ThreadLocal可以在一定程度上减少锁竞争ThreadLocal实现原理:是map实现的,每个线程对应一个key(线程对象)和value(变量副本),每次子线程操作变量,都会与主内存同步由于ThreadLocal是每个线程对应一个key和value,所以说是高并发时,可以避免锁竞争• 单例模式+多线程static inner class 或者 double check instance线程安全的单例模式1> 私有构造器2> 静态内部类-->提供私有静态内部类、私有静态变量2> 双重检查-->提供私有静态实例变量3> 提供获取实例对象方法-->公开静态 getInstance()都是三个步骤:私有构造器、提供变量、提供获取实例的方法普通的单利饿汉模式私有静态变量和构造方法提供公开的获取实例的方法静态内部类单利饿汉模式私有静态变量和构造方法,并将静态变量放在内部类中提供获取实例的方法,返回静态内部类的静态变量• 同步类容器什么是同步类容器?    比如:古老的vector、hashTable,这些容器的同步功能的底层实现就是JDK的synchronized;这并不满足当今的高并发需求,在保证线程安全的同时,也必须要有足够好的性能• 并发类容器Collections.synchronizedMap(new HashMap<String, String>());把普通的HashMap转换成了线程安全的HashMap并发类容器是代替同步类容器的,从而改善了性能;ConcurrentHashMap代替HashTable,并添加了复合操作支持CopyOnWriteArrayList代替Voctor1. ConcurrentMap接口有两个重要实现:ConcurrentHashMap和ConcurrentSkipListMap(支持排序)    实现原理:ConcurrentHashMap使用段来表示不同的部分,每个段(segment)就是一个小的HashTable,它们有自己的锁;    只要多个线程修改操作发生在不同的段上,就可以并发进行;    把一个整体分为16个段,也就是最高支持16个线程,并发修改操作;    这也是在多线程场景时,减少锁的力度从而降低锁的竞争的解决方案;    并且代码中大多用了volatile关键字,目的是第一时间获取修改内容,性能非常好;    注意:如果,有两个线程同时访问了同一个段上的内容,那么就不支持并发了,只能等一个线程释放锁,另一个线程再操作2. Copy-On-Write 简称COWJDK有两种COW容器:CopyOnWriteArrayList和CopyOnWriteArraySet什么是CopyOnWrite容器?    实现原理:CopyOnWrite容器是在写操作时复制的容器,就是往当前容器添加新元素时,不直接添加到当前容器,而是copy出一个新容器并添加新元素,再将旧容器的引用指向新容器,CopyOnWrite是一种读写分离的思想    应用场景:最好在读多 写少场景下应用,如果是写多读少就直接用加锁的形式• 并发QueueJDK提供两种实现:ConcurrentLinkedQueue高性能队列,BlockingQueue阻塞队列,都继承QueueConcurrentLinkedQueue:适用于高并发场景,通过无锁的方式,实现了高并发下的高性能,通常ConcurrentLinkedQueue比BlockingQueue性能高。什么是ConcurrentLinkedQueue?    它是基于链接节点的无界线程安全队列,该队列遵循先进先出原则,头是最先加入,尾是最近加入,该队列不允许null元素ConcurrentLinkedQueue重要的方法?    add()和offer()都是添加操作,没有区别    poll()和peek()都是取头元素节点,区别前者会删除元素,后者不会BlockingQueue接口    ArrayBlockingQueue:基于数组的阻塞队列,是固定长度的,数据缓冲,没有实现读写分离,可以指定先进先出或先进后出,也叫有界队列    LinkedBlockingQueue:基于链表的阻塞队列,之所以能高效并发数据,数据缓冲,是因为实现了读写分离两个锁,生产者和消费者并行运行,无界队列    SynchronousQueue:一种没有缓冲的队列,生产者产生的数据,会被消费者获取并消费    PriorityBlockingQueue:基于优先级的队列,无界队列,在take()时会排序    DelayQueue:带有延迟时间的队列,没有大小限制• 多线程设计模式并行设计模式属于设计优化的一部分,它是对一些常用的多线程结构的总结和抽象。这里主要介绍Future、MasterWorker、生产者-消费  模型• Future模式Future思想类似于ajax,JDK有具体实现future客户端请求,直接返回一个假数据,然后启用一个线程去请求真实数据,当用的时候用的是真实数据futureClient:返回futureData,new子线程加载realDatafutureData: 如果真实数据没有加载好就阻塞,如果加载好了就设置并返回realData:请求真实数据futureData和realData都实现了Data接口• MasterWorker模式是常用的并行计算模式,核心思想是系统由两类进程协调工作(master进程和worker进程)master负责接收和和分配任务,worker负责处理子任务,当worker处理完成后,会将结果返回给master,有master做归纳和总结好处:将一个大任务分为多个小任务并行执行,从而提高系统的吞吐量Master:1 使用ConcurrentLinkedQueue装100个job         2 使用HashMap装Worker(线程)         3 使用ConcurrentHashMap装结果集    需要加的方法:初始化master、添加任务方法、执行任务方法、判断是否完毕、返回结果集Worker:1 实现线程          2 每个worker需要有master的ConcurrentLinkedQueue引用(目的得到任务)          3 每个worker需要master的ConcurrentHashMap引用(目的装结果集)    需要添加的方法:获取任务并执行任务、将结果集装入集合• 生产者-消费者模式通常由两类线程,N个生产者线程和N个消费者线程,生产者负责提交用户请求,消费者负责处理生产者提交的任务,生产者和消费者通过共享内存缓存区通信。• Executor框架有JDK提供的多线程框架,在java.util.concurrent,也是JDK并发包的核心,比较重要的类是Executors(线程工厂),通过Executors可以创建特定功能的线程池。创建线程池的方法:newFixedThreadPool():返回固定数量的线程池,当有任务时,线程池空闲立即执行,不空闲,则被暂缓在一个队列中,等有空闲再执行newSingleThreadExecutor():创建一个线程的线程池,空闲执行,不空闲暂缓在任务队列中newCachedThreadPool():根据情况返回一个可以调整个数的线程池,不限制最大线程数量,有任务创建线程,没有任务不创建线程,并且每一个空闲线程在60s内自动回收newScheduledThreadPool()返回ScheduledExecutorService对象,该线程可以指定线程数量• 自定义线程池若Executors工厂无法满足我们的需求,我们可以自己去创建线程池,Executors工厂创建线程池的方法都是用了ThreadPoolExecutor类• 自定义线程池详解这个构造方法对于队列是什么类型的比较重要1、在使用有界队列时:如果实际线程数 < 核心线程数,则创建线程并执行;如果实际线程数 > 核心线程数,将任务加入队列,如果队列已满,    总线程数不大于maximumPoolSize(最大线程数)的前提下,创建新的线程    总线程数大于maximumPoolSize(最大线程数),则执行拒绝策略,或者其他自定义方式2、在使用无界队列时:与有界队列对比:除非系统资源耗尽,否则无界的任务的队列,不存在任务入队失败的情况。如果当新任务到来时,系统的线程数 < corePoolSize时,则新建线程并执行任务,到达corePoolSize时,就不会再继续增加。如果仍有任务继续增加时,而没有空闲的线程资源,则任务进入队列等待如果任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存JDK拒绝策略:    AbortPolicy:直接抛出异常,组织系统正常工作    CallerRunsPolicy:只要线程池不关闭,该策略直接在调用者线程中,运行当前被丢弃的任务    DiscardOldestPolicy:丢弃最老的一个请求,尝试再次提交当前任务    DiscardPolicy:丢弃无法处理的任务,不给予任何处理,如需要自定义拒绝策略可以实现RejecetExecutionHandler• 线程池实现定时器• Concurrent.util常用类CyclicBarrier:假设每个线程代表一位运动员,当运动员都准备好,才一起出发,只要有一个没有准备好,大家都等待CountDownLacth:经常用于某些初始化操作,等待初始化完毕后,通知主线程继续工作CyclicBarrier和CountDownLacth的区别:前者是针对多个线程,后者针对是一个线程Callable和Runable区别:Callable的call方法有String的返回值,Runable的run方法没有返回值Callable和Future使用:Future非常适合,在处理耗时很长的业务逻辑时,可以有效的减少系统的响应时间,提高系统的吞吐量pv:网站总访问量uv:访问网站的访客qps:每秒访问次数rt:请求的响应时间28原则:80%的请求,将在20%的时间响应峰值qps=(总pvX80%)/(60X60X24X20%)机器数=总峰值qps/压测单台机器最高的qps• 重入锁  Lock和Condition重入锁(ReentrantLock)是一种递归无阻塞的同步机制。需要同步的代码,分别加上锁,但不要忘记最后一定要释放锁,不然会造成锁不会释放,其他线程无法进入Lock和Condition    只有在Lock基础之上会产生(也可以多个Condition)公平锁和非公平锁    Lock lock = new ReentrantLock(boolean isFair)    tryLock():尝试获得锁    isFair():是否是公平锁    isLocked():是否锁定    getHoldCount():查询当前线程,调用lock()的次数    lockInterruptibly():优先响应中断的锁Lock和synchronized的区别是啥?    Lock要比synchronized灵活很多    synchronized是JVM底层实现的、Lock是java代码实现的    synchronized可以自动释放锁、Lock需要手动释放锁    Lock比synchronized多了一些特性:锁投票,定时锁,可中断锁    重点区别:ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)Lock和synchronized使用建议    并发小的情况下使用synchronized    并发高的情况下使用ReentrantLock    http://blog.csdn.net/fw0124/article/details/6672522ReentrantLock比ReentrantReadWriteLock性能高• 读写锁  ReentrantReadWriteLock其核心思想就是读写分离,在高并发下,尤其是读多写少情况下,性能远高于重入锁。之前的synchronize、ReentrantLock,同一时间,只能有一个线程访问被锁的代码;而读写锁不同,本质是分为了两个锁(读锁、写锁),若果是读锁,多个线程可以并发访问,如果是写锁,只能一个个访问口诀:读读共享,写写互斥,读写互斥锁的分类http://www.cnblogs.com/qifengshi/p/6831055.html• Disruptor并发框架是一个高性能异步处理框架,或者可以认为是最快消息框架(轻量的JMS),也可以认为是一个观察者模式或者监听模式。作用:能够在无锁的情况下,实现网络的Queue并发操作开发步骤:1、准备数据:数据对象(模拟数据)、工厂对象(创建数据)、消费者对象(注册监听)、生产者对象(发布数据)           2、创建disruptor对象           3、创建消费者并注册disruptor(disruptor.handleEventsWith(new LongEventHandle())),并启动drsruptor(disruptor.start())           4、获取到ringBuffer(disruptor.getRingBuffer())           5、创建生产者(new LongEventProducer(ringBuffer))           6、生产者将数据发布到disruptor,并disruptor发布到消费者(producer.onData(数据))RingBuffer:disruptor的核心组件(负责存储和更新disruptor中流通的数据)Sequence:disruptor使用sequence来表示一个特殊组件处理的序号,支持AtomicLong类的特性Sequence:disruptor真正核心,实现了这个接口的两种生产者(单生产者和多生产者)均实现了所有的并发算法,是为了在生产者和消费者之间,准确快速的数据传递SequenceBarrier:由Sequencer生成(他决定了消费者是否来消费的Event的逻辑,用于协调生产者和消费者快慢的问题)其他术语:WaitStragegy、Event数据、EventProcessor、EventHandler消费者、Producer、WorkProcessor、WorkerPool、LifecycleAware当生产者快,消费者慢时,生成者阻塞等待,等待消费者将第一个元素,消费完之后,然后才会覆盖填充数据,但是Sequence是递增的当生产者慢,消费者快时,消费者阻塞等待消费者取数据:12%2=2P1同时将person对象不同的部分,分别传给c1和c2处理,最后在统一给c3处理(多消费者,一个生产者模式)还可以这么玩
原创粉丝点击