线程简介

线程安全概念
    当多个线程访问某个类(对象或者方法或者属性)时,这个类始终都能表现出正确的行为,那么这个类就是线程安全的。
synchronized(同步):
1. 可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或者“临界区”。
2. 当多个线程访问某个线程类(同一个对象)的run的方法时,以排队的方式进行处理(这里排队是按照CPU分配的先后顺序而定的),一个线程想要执行synchronized修饰的方法里的代码,首先是尝试获得锁,如果拿到锁,则执行synchronized代码体内容;如果拿不到锁,则这个线程就会不断的尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题. 

多个线程多个锁:
1. 多个线程,每个线程都可以拿到自己指定的锁,分别获得各自锁之后,执行synchrnized方法的内容。
2. 关键字synchrnized取得是锁都是对象锁,而不是把一段代码(方法)当做锁,所以示例代码中那个线程先执行synchronized关键字的方法,那个线程就持有该方法所属对象的锁(Lock),两个对象,线程获得的就是两个不同的锁,他们互不影响。
3. 有一种情况则是相同的锁,即在静态方法上加syschronized关键字,表示锁定.class类,类级别的锁(独占.class类)

ACID是什么:
A:原子性
C: 一致性,例如:oracle的undo,用于回滚、避免脏读
I:隔离性
D:持久性

volatile关键字
1. 主要是使变量在多个线程间可见,但不具备同步性(也就是原子性),可以算上是一个轻量级的synchronized,性能要比synchronized强很多,不会造成阻塞(在很多开源的架构里,比如netty的底层代码就大量使用volatile,可见netty性能一定是非常不错的)。注意:一般volatile用于只针对于多线程可见的变量操作,并不能代替synchronzied的同步功能。
2. volatile关键字只具有可见性,没有原子性。要实现原子性建议使用atomic类的系列对象,支持原子性操作(注意atomic类只保证本身方法原子性,并不保证多次操作的原子性)

ThredLocal概念
1. 线程局部变量,是一种多线程间并发访问变量的解决方案。与其synchronized等加锁的方式不同,ThredLocal完全不提供锁,而使用以空间换时间的手段,为每个线程提供变量的独立副本,以保障线程安全。
2. 从性能上说,ThredLocal不具有绝对的优势,在并发不是很高的时候,加锁的性能会更好,但作为一套与锁完全无关的线程安全解决方案,在高并发量或者竞争激烈的场景,使ThredLocal可以在一定程度上减少锁竞争。

wait/notify实现线程的通信,都是object类的方法
1. wait和nofity必须配合synchronzied关键字使用
2. wait方法释放锁,notity方法不释放锁,sleep也是不释放锁

模拟阻塞Queue,其实可以使用固定长度的List实现

单例模式,最常见的就是饥饿模式,和懒汉模式,

多线程中最安全的单例代码:
//饿汉模式,静态内部类方式
package cn.xxx.xx
public class Singleton{
  //静态私有成员,已初始化
  private static Singleton instance = new Singleton();
  
  //使用private修饰作用是外部不能直接new Singleton()创建实例,只能使用getInstance()方法创建实例
  private Singleton(){};

  public Singleton getInstance(){
    return instance;
  }
}

//懒汉模式,double check方式
package cn.xxx.xxx
public class Singleton{
   //对保存实例的变量添加volatile的修饰
   private volatile static Singleton instance = null;

   //使用private修饰作用是外部不能直接new Singleton()创建实例,只能使用getInstance()方法创建实例
   private Singleton(){}
	
   public static Singleton getInstance(){
      //先检查实例是否存在,如果不存在才进行下面的同步块
      if(instance == null){
         //同步块,线程安全的创建实例
         synchronized(Singleton.class){
            //再次检查实例是否存在,如果不存在才直正创建实例
            if(instance == null){
               instance = new Sinleton();
            }
         } 
      }
     return instance;
   }
}

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

同步类容器:
Vector Hashtable都是线程安全,这些容器的同步功能其实都是由jdk的Collections.synchronized()实现同步的,其底层的机制无非是使用传统的synchronized关键字对每个公用方法都进行同步,使得每次只能有一个线程访问容器的状态。这明显不满足我们今天互联网时代高并发的需求,在保证线程安全的同时,也必须有足够好的性能。,

并发类容器:
jdk5.0以后提供了多种并发类容器来替代同步容器从而改善性能。同步容器的状态都是串行化的,他们虽然实现了线程安全,但是严重降低了并发性,在多线程环境时,严重降低了应用程序的吞吐量。

并发类容器是专门针对并发设计的,使用ConcurrentHashMap来代替给予散列的传统HashtTable,而且在ConcurrentHashMap中,添加了一些常见复合操作的支持,以及使用了CopyOnWriteArrayList代替Vector,ConcurrentLinkedQueue和LinkedBlockQueue,前者是高性能的队列,后者是以阻塞形式的队列,具体实现Queue还有很多,例如:ArrayBlockingQueue、PriorityBlockQueue、SynchronousQueue等。

ConcurrentMap接口两个实现
1. ConcurrentHashMap,是hashtable加强版
2. ConcurrentSkipListMap,相当于treeMap

ConcurrentHashMap内部使用段(Segment. 来表示这些不同的部分,每个段其实就是一个小的HashTable,它们有自己的锁,只要多个修改操作发生在不同的段上,它们就可以并发进行。把整体分成16个段,也就是最高支持16个线程的并发修改操作。

ConcurrentHashMap减少锁的粒度,从而减少锁竞争的一种方案。并且代码中大多共享变量使用volatile关键字声明,目的是第一时间获取修改的内容,性能非常好。

Copy-On-Write简称COW,是一种用于程序设计中的优化策略。高并发读操作不加锁,写操作使用了ReentrantLock(重入锁)。实现读写分离的一种方案,适用于读多写少场景。
opy-On-Write两个实现:
CopyOnWriteArrayList
CopyOnWriteArraySet

两种代表性Queue:ConcurrentLinkedQueue(无阻塞)和BlockQueue(阻塞) 

几种队列类介绍:
1. ConcurrentLinkedQueue:高性能无阻塞无界队列
2. ArrayBlockingQueue:基于数组的、有界的、没有读写分离的阻塞队列,意味着生产和消费不能完全并行。
3. LinkedBlockingQueue:基于链表的、无界的、有读写分离的阻塞队列,能高效处理并发数据,意味着生产和消费能完全并行。此队列有drainto()方法
4. SynchronousQueue:SynchronousQueue:一种没有缓冲的队列,生产者生产的数据直接会被消费者获取并消费。结合生产消费者模式使用,如果只put()没有take()的话,     程序会一直阻塞,直到被消费为止才往下执行。
5. PriorityBlockingQueue:基于优先级的、无界的阻塞队列,需要实现Comparable接口,采用公平锁,
6. DeayQueue:带延迟时间的、无界的Queue,

队列方法:
add()    //添加数据
offer()  //添加数据,可以指定添加使用多长时间,
  例如:queue.offer(data,2,TimeUnit.SECONDS),两秒后还没把数据加到队列就返回false
put()    //添加数据
peen()   //从头部取数据,不删除数据
poll()   //从头部取数据,删除数据
take()   //取数据

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

future、Master-Worker和生产者-消费者,3个设计模式
1. future模式有点类似于网上商品订单,付钱了就等着收货,类似于ajax,异步的思想。
2. Master-Worker模式是常用的并行计算模式。类似于hadoop、storm,它的核心思想由两类进程写作工作:Master进程和Worker进程。Master分配任务,Worker执行任务返回给Master总结,提高吞吐量。
3. 生产者-消费者也是一个非常经典的多线程模式,类似于MQ。

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

线程池,Executors的4个静态方法、自定义线程池
Executors是工厂类:例如:ExecutorService cachePool = Executors.newCachedThreadPool();
1. newFixedThreadPool()方法,该方法返回一个固定数量的线程池,该方法的线程数始终不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中等待有空闲的线程去执行
2. newSingleThreadExecutor()方法,创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中,很少场景会用到。
3. newCachedThreadPool()方法,返回一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若用空闲的线程则执行任务,若无任务则不创建线程并且每一个空闲线程会在60秒后自动回收。
4. newScheduleThreadPool()方法,该方法返回一个SecheduleExecutorService对象,但该线程可以指定线程的数量,相当于定时器,很少会使用,现在都使用spring quartz实现定时执行任务。

这四个静态方法的核心底层都是通过new ThreadPoolExecutor()实例化出来的,因传入的参数不同,所以才有不同的特性。

自定义线程池
1. 使用有界队列,有拒绝策略
2. 使用无界队列,LinkedBlockingQueue,无拒绝策略

Executors的submit和executor区别:
1. summit可以传入实现Callable接口的实例对象
2. submit方法有返回值,executor方法没有返回值

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

8. Concurrent.util工具类
   1. CountDownLatch:他经常用于监听某些初始化操作,等初始化执行完毕后,通知主线程继续工作。
      例如:static final CountDownLatch c = new CountDownLatch(1);  await()和countDown()
   2. CyclicBarrier:运动员跑步场景,所以运动员都准备好了,发令员才吹哨,所以运动员才开始跑步(执行. 。
   3. CountDownLatch和CyclicBarrier区别:CountDownLatch只针对一个线程,CyclicBarrier针对多个线程。
   4. Callable和Future使用:Future模式非常适合在处理很耗时很长的业务逻辑进行使用,可以有效的减少系统响应时间,提高系统的吞吐量。
   5. Semaphore信号量:指定同时只能多少个线程一起执行,可以控制系统的流量
   6. 容量评估:一般来说通过开发、运维、测试、以及业务等相关干系人,综合出系统的一系列阈值,然后我们根据关键阈值如qps、rt等,对系统进行有效的变更。采用80/20原则,即80%的访问请求将在20%的时间内到达。

9. 重入锁、读写锁使用
   1)重入锁:ReentrantLock:在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁定,不然会造成锁永远无法释放,其他线程永远进不来的结果。一般都是配合try,finally来使用,在finally中释放锁定。默认是非公平锁,效率比公平锁高。
     private Lock lock = new ReentrantLock();
   2)读写锁:ReentrantReadWriteLock:核心是实现读写分离的锁,适用于读多写少,性能要远高于重入锁。读读共享,写写互斥,读写互斥。
   3)Condition:我们可以通过一个Lock对象产生多个Condition进行多线程间的交互,非常的灵活。可以使得部分需要唤醒的线程唤醒,其他线程则继续等待通知。

synchronized和lock区别:
1. jdk1.8对synchronized优化,效率跟lock差不多,lock更灵活
2. synchronized必须要配合object的wait和notity()、notityAll()使用;lock配合多个Condition使用,非常灵活

锁优化总结:
1. 避免死锁
2. 减少锁的持有时间
3. 减少锁的粒度
4. 锁的分离
5. 尽量使用无锁的操作,如原子操作(Atomic系列类),volatile关键字

如何解决高并发:
1)网络端:动静态化,例如:js css文件、图片等
2)服务器:业务规划、nginx负载

3)Java:限流,使用Semaphore(少使用)、redis(缓存+过期时间,防止恶意攻击)

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

Disruptor介绍:
    Martin Fowler在自己网站上写了一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金融交易平台,它能够以很低的延迟产生大量交易。这个系统是建立在JVM平台上,其核心是一个业务逻辑处理器,它能够在一个线程里每秒处理6百万订单。业务逻辑处理器完全是运行在内存中,使用事件源驱动方式。业务逻辑处理器的核心是Disruptor。
    Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作。
    Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。 

在Disruptor中,我们想实现hello world 需要如下几步骤:
1. 建立一个Event类
2. 建立一个工厂Event类,用于创建Event类实例对象
3. 需要有一个监听事件类,用于处理数据(Event类)
4. 我们需要进行测试代码编写。实例化Disruptor实例,配置一系列参数。然后我们对Disruptor实例绑定监听事件类,接受并处理数据。
5. 在Disruptor中,真正存储数据的核心叫做RingBuffer,我们通过Disruptor实例拿到它,然后把数据生产出来,把数据加入到RingBuffer的实例对象中即可。

HAProxy是一个使用C语言编写的自由及开放源代码软件[1],其提供高可用性、负载均衡,以及基于TCP和HTTP的应用程序代理

LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统。本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一。


猜你喜欢

转载自blog.csdn.net/romantic_pk/article/details/60338652
今日推荐