王筝的博客
ruby学习
MySQL binlog的格式有三种:
  1. 基于SQL语句的复制(statement-based replication, SBR)
  2. 基于行的复制(row-based replication, RBR)
  3. 混合模式复制(mixed-based replication, MBR)
相应地,binlog的格式也有三种:STATEMENT,ROW,MIXED。它主要用于mysql的复制技术。
idb基本上都是供的基于行的复制。
  1. 基于SQL语句的复制(statement-based replication, SBR), (1) 优点: 历史悠久,技术成熟。 产生的binlog文件较小,比较节省空间。 binlog中包含了所有数据库更改信息,可以据此来审核数据库的安全等情况。 binlog可以用于实时的还原,而不仅仅用于复制。 主从版本可以不一样,从服务器版本可以比主服务器版本高。 (2) 缺点: 不是所有的UPDATE语句都能被复制,尤其是包含不确定操作的时候。 调用具有不确定因素的 UDF 时复制也可能出问题 使用以下函数的语句也无法被复制:
  • LOAD_FILE()
  • UUID()
  • USER()
  • FOUND_ROWS()
  • SYSDATE() (除非启动时启用了 –sysdate-is-now 选项) INSERT … SELECT 会产生比 RBR 更多的行级锁
2.基于行的复制(row-based replication, RBR), (1)优点: 任何情况都可以被复制,这对复制来说是最安全可靠的 多数情况下,从服务器上的表如果有主键的话,复制就会快了很多 复制以下几种语句时的行锁更少:
  • INSERT … SELECT
  • 包含 AUTO_INCREMENT 字段的 INSERT
  • 没有附带条件或者并没有修改很多记录的 UPDATE 或 DELETE 语句 执行 INSERT,UPDATE,DELETE 语句时锁更少 从服务器上采用多线程来执行复制成为可能。
(2)缺点: binlog 文件太大 复杂的回滚时 binlog 中会包含大量的数据 主服务器上执行 UPDATE 语句时,所有发生变化的记录都会写到 binlog 中,而 SBR 只会写一次,这会导致频繁发生 binlog 的并发写问题 UDF 产生的大 BLOB 值会导致复制变慢 无法从 binlog 中看到都复制了写什么语句,无法进行审计。
  1. 混合模式复制(mixed-based replication, MBR)。
是上面两种方式的折中,对于能用
对应的,binlog的格式也有三种:STATEMENT,ROW,MIXED。

 

内存分配机制

mc内存分配机制简介
memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存,Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块,以完全解决内存碎片问题。
先来解释一下与Slab Allocator存储有关的几个术语:
Page:分配给Slab的内存空间,默认是1MB。分配给Slab之后根据slab的大小切分成chunk。 Chunk:用于缓存记录的内存空间。 Slab Class:特定大小的chunk的组。
Growth Factor:增长因数,默认为1.25(较早的版本固定为2)
mc启动后,会根据这个factor,计算出从1M逐步递减的不同的slab,如factor=1.25时:
slab class   1: chunk size     88 perslab 11915  
slab class   2: chunk size    112 perslab  9362  
slab class   3: chunk size    144 perslab  7281  
slab class   4: chunk size    184 perslab  5698  
slab class   5: chunk size    232 perslab  4519  
slab class   6: chunk size    296 perslab  3542  
slab class   7: chunk size    376 perslab  2788  
slab class   8: chunk size    472 perslab  2221  
slab class   9: chunk size    592 perslab  1771  
slab class  10: chunk size    744 perslab  1409
第一列数据(slab class),为slab的编号;
第二列数据是chunk的大小,跟slab class是一一对应的关系,可以通俗的理解为slab就是存放一组相同大小chunk的集合,只不过这个集合是固定的(1M),
第三列数据,表示每种不同slab中的page可以存放的chunk个数,实际上等于1MB/ (chunk size),例如slab1中的chunk size是88B,那么这种slab中每个page中可以存放的chunk个数为 1MB / 88B ,约等于11915
很显然,slab的chunk size越大,其中的每个page包含的chunk数量就越少
如图所示:
新入对象时,会根据自身大小来匹配slab列表,比如100KB的对象,根据最小空间损失原则,会被放入到slab2(size:112B)对应的page下,如下图
这时,如果slab2下的page中有尚可以使用的chunk(即空闲的chunk或者过期的chunk),slab2会优先使用这些chunk,在没有chunk可用的情况下,mc会去内存中再申请一个page,然后切分成chunk,然后使用;需要注意的是,根据 Slab Allocator算法, 该实例中的100KB对象,是永远没有机会存放到其他slab(如slab3,slab4等等),即便是其他slab中有大量的可用chunk,细心的朋友会发现,这种机制很有可能会导致内存浪费严重,mc命中率降低等问题,对,这种问题真的存在,这也正是这种机制的缺点,下面会进行详细的分析和探讨
mc数据删除机制简介:
首先我们知道,缓存在mc中的数据,不会主动从内存中消失,也就是说mc不会监视记录是否过期,而是在client端执行get方法时才去检查时间戳是否过期(这样做的目的就是避免在过期监视上耗费cpu资源,以提高mc的响应能力);每次有新对象加入时,mc会优先将对象置于已超时的同一规格chunk中,然而,即使如此,内存仍然会发生追加新记录时空间不足的情况,那么,当mc内存耗完后,又是怎样处理新入的数据呢?mc有两种处理策略,一种是默认的LRU(Least Recently Used),指删除近段时间最少使用同规格chunk,再将对象塞入),另一种策略是存满即不可再存,除非有过期的对象,否则会报错

一致性哈希原理

synchronized

This version is the fewest lines of code, largely because the synchronization mechanism used is built into the language and runtime. But the programmer has to remember to avoid a couple of common bugs: The wait() must be inside a while instead of an if, and notifyAll() must be used instead of notify() because there are two different logical conditions being awaited.
   public class SafeBox<V> {
     private V value;

     public synchronized V get() throws InterruptedException {
       while (value == null) {
         wait();
       }
       V result = value;
       value = null;
       notifyAll();
       return result;
     }

     public synchronized void set(V newValue) throws InterruptedException {
       while (value != null) {
         wait();
       }
       value = newValue;
       notifyAll();
     }
   }

ReentrantLock

This version is much more verbose than the synchronized version, and still suffers from the need for the programmer to remember to use while instead of if. However, one advantage is that we can introduce two separate Condition objects, which allows us to use signal() instead of signalAll(), which may be a performance benefit.

   public class SafeBox<V> {
     private final ReentrantLock lock = new ReentrantLock();
     private final Condition valuePresent = lock.newCondition();
     private final Condition valueAbsent = lock.newCondition();
     private V value;

     public V get() throws InterruptedException {
       lock.lock();
       try {
         while (value == null) {
           valuePresent.await();
         }
         V result = value;
         value = null;
         valueAbsent.signal();
         return result;
       } finally {
         lock.unlock();
       }
     }

     public void set(V newValue) throws InterruptedException {
       lock.lock();
       try {
         while (value != null) {
           valueAbsent.await();
         }
         value = newValue;
         valuePresent.signal();
       } finally {
         lock.unlock();
       }
     }
   }

 

 

背景

现实场景

  1. 单个节点的容量达到上限,无法继续单点增加内存,如何解决?
  2. 单个节点支撑的QPS达到上限,如何解决?

初步方案

增加N个缓存节点,为了保证缓存数据的均匀,一般情况会采用对key值hash,然后取模的方式,然后根据结果,确认数据落到哪台节点上:如下:

hash(key)%N

很好,这个的确解决了上面的问题,实现了初步的分布式存储,数据均匀分散到了各个节点上,流量请求也均匀的分散到了各个节点。

方案问题

  1. 某台服务器突然宕机。缓存服务器从N变为N-1台。 hash(key)%(N-1)
  2. 缓存容量达到上限或者请求处理达到上限,需要增加缓存服务器,假定增加1台,则缓存服务器从N变为N+1。hash(key)%(N+1)

增加或者删除缓存服务器的时候,意味着大部分的缓存都会失效。这个是比较致命的一点,缓存失效,如果业务为缓存不命中,查询DB的话,会导致一瞬间DB的压力陡增。可能会导致整个服务不可用。

目标

增删机器时,希望大部分key依旧在原有的缓存服务器上保持不变。举例来说:key1,key2,key3原先再Cache1机器上,现在增加一台缓存服务器,希望key1,key2,key3依旧在Cache1机器上,而不是在Cache2机器上。

原理

基本概念

一致性hash算法是希望在增删节点的时候,让尽可能多的数据不失效。

判断hash算法好坏的四个标准:

  • 平衡性:平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。
  • 单调性:单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
  • 分散性:在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。
  • 负载:负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同 的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。

 

使用常见的hash算法可以把一个key值哈希到一个具有2^32个桶的空间中。也可以理解成,将key值哈希到 [0, 2^32] 的一个数字空间中。 我们假设这个是个首尾连接的环形空间。

一致性Hash算法

  1. 构造hash环空间
  2. 把节点(服务器)映射到hash环
  3. 把数据对象映射到hash环
  4. 把数据对象映射到节点

 

把数据用hash函数(如MD5,CRC32),映射到一个很大的空间里,如图所示。数据的存储时,先得到一个hash值,对应到这个环中的每个位置,如k1对应到了图中所示的位置,然后沿顺时针找到一个机器节点B,将k1存储到B这个节点中。

如果B节点宕机了,则B上的数据就会落到C节点上,也就是说只有C节点受到影响,也就意味着解决了最开始的方案中可能的雪崩问题。如下图所示:

上面的简单的一致性hash的方案在某些情况下但依旧存在问题:一个节点宕机之后,数据需要落到距离他最近的节点上,会导致下个节点的压力突然增大,可能导致雪崩,整个服务挂掉。

  1. 之前请求到B上的流量转嫁到了C上,会导致C的流量增加,如果之前B上存在热点数据,则可能导致C扛不住压力挂掉。
  2. 之前存储到B上的key值转义到了C,会导致C的内容占用量增加,可能存在瓶颈。

当上面两个压力发生的时候,可能导致C节点也宕机了。那么压力便会传递到D上,又出现了类似滚雪球的情况,服务压力出现了雪崩,导致整个服务不可用。

虚拟节点

图中的A1、A2、B1、B2、C1、C2、D1、D2都是虚拟节点,机器A负载存储A1、A2的数据,机器B负载存储B1、B2的数据,机器C负载存储C1、C2的数据。由于这些虚拟节点数量很多,均匀分布,因此不会造成雪崩现象。

虚拟节点的 hash 计算可以采用对应节点的 IP 地址加数字后缀的方式。例如假设 cache A 的 IP 地址为202.168.14.241 。

引入虚拟节点前,计算 cache A 的 hash 值:

Hash(“202.168.14.241”);

引入虚拟节点后,计算虚拟节点cache A1 和 cache A2 的 hash 值:

Hash(“202.168.14.241#1”);  // cache A1

Hash(“202.168.14.241#2”);  // cache A2

实际节点的N个虚拟节点尽量随机分布在整数增加cache,就能尽量保到新cache点的key来自于不同的cache从而保证负载均衡.

hash(key) -> 虚拟节点 -> 真实节点

JAVA实现

public class Shard<S> { // S类封装了机器节点的信息 ,如name、password、ip、port等

    private TreeMap<Long, S> nodes; // 虚拟节点
    private List<S> shards; // 真实机器节点
    private final int NODE_NUM = 100; // 每个机器节点关联的虚拟节点个数

    public Shard(List<S> shards) {
        super();
        this.shards = shards;
        init();
    }

    private void init() { // 初始化一致性hash环
        nodes = new TreeMap<Long, S>();
        for (int i = 0; i != shards.size(); ++i) { // 每个真实机器节点都需要关联虚拟节点
            final S shardInfo = shards.get(i);

            for (int n = 0; n < NODE_NUM; n++)
                // 一个真实机器节点关联NODE_NUM个虚拟节点
                nodes.put(hash("SHARD-" + i + "-NODE-" + n), shardInfo);

        }
    }

    public S getShardInfo(String key) {
        SortedMap<Long, S> tail = nodes.tailMap(hash(key)); // 沿环的顺时针找到一个虚拟节点
        if (tail.size() == 0) {
            return nodes.get(nodes.firstKey());
        }
        return tail.get(tail.firstKey()); // 返回该虚拟节点对应的真实机器节点的信息
    }

    /**
     *  MurMurHash算法,是非加密HASH算法,性能很高,
     *  比传统的CRC32,MD5,SHA-1(这两个算法都是加密HASH算法,复杂度本身就很高,带来的性能上的损害也不可避免)
     *  等HASH算法要快很多,而且据说这个算法的碰撞率很低.
     *  http://murmurhash.googlepages.com/
     */
    private Long hash(String key) {

        ByteBuffer buf = ByteBuffer.wrap(key.getBytes());
        int seed = 0x1234ABCD;

        ByteOrder byteOrder = buf.order();
        buf.order(ByteOrder.LITTLE_ENDIAN);

        long m = 0xc6a4a7935bd1e995L;
        int r = 47;

        long h = seed ^ (buf.remaining() * m);

        long k;
        while (buf.remaining() >= 8) {
            k = buf.getLong();

            k *= m;
            k ^= k >>> r;
            k *= m;

            h ^= k;
            h *= m;
        }

        if (buf.remaining() > 0) {
            ByteBuffer finish = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
            finish.put(buf).rewind();
            h ^= finish.getLong();
            h *= m;
        }

        h ^= h >>> r;
        h *= m;
        h ^= h >>> r;

        buf.order(byteOrder);
        return h;
    }

}

 

应用

  1. memcache、redis服务器等缓存服务器的负载均衡(分布式cache);
  2. MySQL的分布式集群(分布式DB);
  3. 大量session的共享存储(分布式文件,或session服务器等)。

twemproxy 是 twitter 开源的一个轻量级的后端代理,兼容 redis/memcache 协议,可用以管理 redis/memcache 集群。

twemproxy 内部有实现一致性哈希算法,对于客户端而言,twemproxy 相当于是缓存数据库的入口,它无需知道后端的部署是怎样的。twemproxy 会检测与每个节点的连接是否健康,出现异常的节点会被剔除;待一段时间后,twemproxy 会再次尝试连接被剔除的节点。

通常,一个 Redis 节点池可以分由多个 twemproxy 管理,少数 twemproxy 负责写,多数负责读。twemproxy 可以实时获取节点池内的所有 Redis 节点的状态,但其对故障修复的支持还有待提高。解决的方法是可以借助 redis sentinel 来实现自动的主从切换,当主机 down 掉后,sentinel 会自动将从机配置为主机。而 twemproxy 可以定时向 redis sentinel 拉取信息,从而替换出现异常的节点。

 

Java中的线程的生命周期大体可分为5种状态。
  1. 新建(NEW):新创建了一个线程对象。
  2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
  3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
  4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种: (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。 (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。 (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
  5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
几个方法的比较:
  1. Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入阻塞,但不释放对象锁,millis后线程自动苏醒进入可运行状态。作用:给其它线程执行机会的最佳方式。
  2. Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的cpu时间片,由运行状态变会可运行状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞
  3. t.join()/t.join(long millis),当前线程里调用其它线程1的join方法,当前线程阻塞,但不释放对象锁,直到线程1执行完毕或者millis时间到,当前线程进入可运行状态。
  4. obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout)timeout时间到自动唤醒。
  5. obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。

 

 

 

thread中的三个方法:interrupt()、interrupted()、isInterrupted()
1、interrupt()
public void interrupt() {
  if (this != Thread.currentThread())
    checkAccess();
 
  synchronized (blockerLock) {
    Interruptible b = blocker;
    if (b != null) {
    interrupt0();    // Just to set the interrupt flag
    b.interrupt();
    return;
    }
  }
  interrupt0();
  }


/* Some private helper methods */
private native void setPriority0(int newPriority);
private native void stop0(Object o);
private native void suspend0();
private native void resume0();
private native void interrupt0();
分两部分看:
(1)第一部分的第8行注释说得很清楚了,interrupt0()方法的作用是”Just to set the interrupt flag”,即方法的作用仅仅是设置中断标识位
(2)第二部分的第6行就是interrupt0()方法的原型,由于方法是被native修饰的,很明显这是一个本地方法,是Java虚拟机实现的。
当对一个线程,调用 interrupt() 时,
  • 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。
  • 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true。被设置中断标志的线程将继续正常运行,不受影响。
2、isInterrupted()
方法唯一的作用只是测试线程是否已经中断,中断标识位的状态并不受到该方法的影响,看一下Java是如何实现这个方法的:
/**
 * Tests whether this thread has been interrupted. The <i>interrupted
 * status</i> of the thread is unaffected by this method.
 *
 * <p>A thread interruption ignored because a thread was not alive 
 * at the time of the interrupt will be reflected by this method 
 * returning false.
 *
 * @return <code>true</code> if this thread has been interrupted;
 *     <code>false</code> otherwise.
 * @see   #interrupted()
 * @revised 6.0
 */
public boolean isInterrupted() {
return isInterrupted(false);
}

private native boolean isInterrupted(boolean ClearInterrupted);
注意一下第一部分的第2行和第3行,”The interrupted statis of the thread is unaffected by this method”,即线程的中断状态不受到这个方法的影响。最终调用的是isInterrupted(boolean ClearInterrupted),这个方法是一个native的,看得出也是Java虚拟机实现的。方法的参数ClearInterrupted,顾名思义,清除中断标识位,这里传递false,明显就是不清除。
3、interrupted()
方法的作用是测试当前线程是否已经中断,线程的中断标识位由该方法清除。换句话说,连续两次调用该方法的返回值必定是false。看一下这个方法是如何实现的:
/**
 * Tests whether the current thread has been interrupted. The
 * <i>interrupted status</i> of the thread is cleared by this method. In
 * other words, if this method were to be called twice in succession, the
 * second call would return false (unless the current thread were
 * interrupted again, after the first call had cleared its interrupted
 * status and before the second call had examined it).
 *
 * <p>A thread interruption ignored because a thread was not alive 
 * at the time of the interrupt will be reflected by this method 
 * returning false.
 *
 * @return <code>true</code> if the current thread has been interrupted;
 *     <code>false</code> otherwise.
 * @see #isInterrupted()
 * @revised 6.0
 */
public static boolean interrupted() {
return currentThread().isInterrupted(true);

private native boolean isInterrupted(boolean ClearInterrupted);
而如果一个线程被设置中断标识后,进行了一些处理后选择继续进行任务, 而且这个任务也是需要被中断的,那么当然需要清除标志位了。

example

org.springframework.scheduling.concurrent.ExecutorConfigurationSupport
注意代码中Thread.currentThread().interrupt();的用法。
	/**
	 * Perform a shutdown on the underlying ExecutorService.
	 * @see java.util.concurrent.ExecutorService#shutdown()
	 * @see java.util.concurrent.ExecutorService#shutdownNow()
	 * @see #awaitTerminationIfNecessary()
	 */
	public void shutdown() {
		if (logger.isInfoEnabled()) {
			logger.info("Shutting down ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
		}
		if (this.waitForTasksToCompleteOnShutdown) {
			this.executor.shutdown();
		}
		else {
			this.executor.shutdownNow();
		}
		awaitTerminationIfNecessary();
	}

	/**
	 * Wait for the executor to terminate, according to the value of the
	 * {@link #setAwaitTerminationSeconds "awaitTerminationSeconds"} property.
	 */
	private void awaitTerminationIfNecessary() {
		if (this.awaitTerminationSeconds > 0) {
			try {
				if (!this.executor.awaitTermination(this.awaitTerminationSeconds, TimeUnit.SECONDS)) {
					if (logger.isWarnEnabled()) {
						logger.warn("Timed out while waiting for executor" +
								(this.beanName != null ? " '" + this.beanName + "'" : "") + " to terminate");
					}
				}
			}
			catch (InterruptedException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Interrupted while waiting for executor" +
							(this.beanName != null ? " '" + this.beanName + "'" : "") + " to terminate");
				}
				Thread.currentThread().interrupt();
			}
		}
	}

 

JDK7提供了7个阻塞队列。分别是
  • ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
  • PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列。
  • SynchronousQueue:一个不存储元素的阻塞队列。
  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
  • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
常用方法:
   add        增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
  remove   移除并返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException异常
  element  返回队列头部的元素             如果队列为空,则抛出一个NoSuchElementException异常
  offer       添加一个元素并返回true       如果队列已满,则返回false
  poll         移除并返问队列头部的元素    如果队列为空,则返回null
  peek       返回队列头部的元素             如果队列为空,则返回null
  put         添加一个元素                      如果队列满,则阻塞
  take        移除并返回队列头部的元素     如果队列为空,则阻塞
ArrayBlockingQueue和LinkedBlockingQueue的区别和使用场景
相同点:
  • LinkedBlockingQueue和ArrayBlockingQueue都是可阻塞的队列
  • 内部都是使用ReentrantLock和Condition来保证生产和消费的同步;
  • 当队列为空,消费者线程被阻塞;当队列装满,生产者线程被阻塞;
锁机制不同:
  • LinkedBlockingQueue中的锁是分离的,生产者的锁PutLock,消费者的锁takeLock
  • 而ArrayBlockingQueue生产者和消费者使用的是同一把锁;
底层实现机制不同:
  • LinkedBlockingQueue内部维护的是一个链表结构,在生产和消费的时候,需要创建Node对象进行插入或移除,大批量数据的系统中,其对于GC的压力会比较大
  • ArrayBlockingQueue内部维护了一个数组,在生产和消费的时候,是直接将枚举对象插入或移除的,不会产生或销毁任何额外的对象实例
SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合于传递性场景,比如在一个线程中使用的数据,传递给另外一个线程使用,SynchronousQueue的吞吐量高于LinkedBlockingQueue 和 ArrayBlockingQueue。
深度方面:
基础:

1、事务4个特征介绍;acid
2、排序算法;
3、简单描述2、3个设计模式
4、乱码问题如何解,utf8和gbk编码本质区别是什么?
5、一致性hash算法描述?
6、冥等操作用于那些场景?(如何防止重复提交)
7、序列化思路有那些?
8、数据加密协议有那些?
9、http协议有那些部分组成?
10、字符流和字节流的区别?
java:
1、JUC库包括那些核心类?详细讲解其中几个。
参考:
2、多线程由那些类可以实现?
参考:

3、classload流程讲解。
4、代理模式实现方式有那些?
5、nio的核心原理是什么?
6、util包有那些核心类?列举几个详细讲解实现。
7、常用的java集合类、java runtime异常
8、线程安全如何实现?
9、观察者模式描述?java有那些类
广度方面:
1、开源web框架熟悉那些?
参考:springMvc,struct,
2、开源中间件熟悉使用并知晓原理那些,同级功能性能对比。
参考:例如xml解析中间件;通信框架mina、Hessian、Netty对比
3、数据存储方案有那些?
参考:DB、NoSQL、CDN等,具体范围可以在细化如何实现
4、数据序列化方式知晓那些?
参考:java实现,hession,ProtoBuf 等
5、数据加密解密方式知晓那些?
参考:md5,rsa,
6、数据传输协议知晓那些?
参考:soap,wml,等
7、离线计算框架,实时计算框架知晓那些?
8、项目管理工具使用那些?绘图、设计工具使用那些?
9、大并发系统设计有那些方法?
项目经验
1、项目架构设计分析,了解最满意1-2个详细讲解探讨。
2、项目瓶颈、严重问题分析路线讲解,及其对应解决方案。
3、项目并发度,用户量数据,严重故障处理容灾方案。
文化氛围:
1、爱好那些运动或个人喜好?有什么业余的爱好?
2、性格外内向初步判断。
3、做事分析方式,处理态度初步了解。
4、团队贡献,处理团队氛围等。
5、在团队中自己的优势/缺点是什么?对之前团队做个评价(好/坏两方面)
6、评价下历任老板,学到了什么?
7、有5年的规划或想法吗?(生活,工作都行)
8、为什么选择我们这个岗位?(期望)
9、为什么辞去之前的工作?

 

import com.google.common.base.Stopwatch;
开始计时
Stopwatch stopwatch = Stopwatch.createStarted();

停止计时
stopwatch.stop();


计算出来的rt就是

stopwatch.elapsed(TimeUnit.MILLISECONDS)
日志输出样例
schedulingLogger.info("refresh[{}], gray:[{}], resultMap.size:[{}], elapsed(ms):[{}]",
dbEventMessage.getDbEnum().getTableName(), gray, positionTagAndTerminalMap.size(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));

 

 

 

old
taskReportDataDTO.setUv(Long.valueOf(hbaseData.get(HbaseColumnEnum.uv).toString()));
new

taskReportDataDTO.setPv(MapUtils.getLong(hbaseData, HbaseColumnEnum.pv.name().toLowerCase(), 0L));