Concurrency

CountDownLatch

    CountDownLatch countdownlatch = new CountDownLatch(1); // The difinition count is 1
    System.out.println("Count: " + countdownlatch.getCount()); // 1

    new Thread(() -> {
        try {
            countdownlatch.await(); // Current thread enter await, until count reduced to 0
            System.out.println("Count: " + countdownlatch.getCount()); // 0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();

    Thread.sleep(1000);
    System.out.println("Count down");
    countdownlatch.countDown(); // down 1

CyclicBarrier

    public class CyclicBarrierExample {

        private static LongAdder count = new LongAdder();
        private static ExecutorService executorService = Executors.newFixedThreadPool(5);
        private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> System.out.println(count.longValue()));

        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                executorService.execute(CyclicBarrierExample::add);
            }
            executorService.shutdown();
        }
        
        private static void add() {
            try {
                count.increment();
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

Semaphore

    Semaphore semaphore = new Semaphore(5);
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire(); // Get a semaphore
                semaphore.release(); // Release a semaphore
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
       Thread.sleep(1000);
    System.out.println("Available semaphore: " + semaphore.availablePermits()); // 5
    Semaphore semaphore = new Semaphore(5);
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire();
                //semaphore.release(); // Not release
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
    Thread.sleep(1000);
    System.out.println("Available semaphore: " + semaphore.availablePermits()); // 0
    Semaphore semaphore = new Semaphore(5);
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire();
                //semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
    Thread.sleep(1000);
    System.out.println("Available semaphore: " + semaphore.availablePermits()); // 0
    semaphore.acquire(); // Because available semaphore is 0, threa enters await status, until available gt 0
    System.out.println("END"); // Always not output END

Atomic

AtomicLong and LongAdder

Jdk8 update LongAdder and DoubleAdder

Under low update contention, the two classes have similar characteristics. But under
high contention, expected throughput of this class is significantly higher, at the expense of higher space consumption.

Recommend use 'LongAdder' and 'DoubleAdder',
But may not be safe of reading(update value when reading), AtomicLong is safer, its principle is CAS(compareAndSwap)

AtomicXX

@Slf4j
@ThreadSafe
    public class AtomicTest {
        
        private final static int threadTotal = 200;
        private final static int clientTotal = 5000;
        // private static AtomicLong count = new AtomicLong(0);
        private static LongAdder count = new LongAdder(); //Initial value is 0

        public static void main(String[] args) throws Exception {
            ExecutorService executor = Executors.newCachedThreadPool();
            Semaphore semaphore = new Semaphore(threadTotal);
            CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
            log.info("CountdownLatch count: {}", countDownLatch.getCount());

            new Thread(() -> {
                try {
                    countDownLatch.await();
                    executor.shutdown();
                    log.info("CountdownLatch count: {}", countDownLatch.getCount());
                    log.info("count: {}", count.longValue());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }).start();

            Thread.sleep(1000);
            for (int i = 0; i < clientTotal; i++) {
                executor.execute(() -> {
                    try {
                        semaphore.acquire();
                        add();
                        semaphore.release();
                        countDownLatch.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }

        private static void add() {
            count.increment();
        }
    }

AtomicReference

An object reference that may be updated atomically.

    public class AtomicReferenceTest {

        @Data
        private static class User {
            private String name;
        }

        public static void main(String[] args) {
            AtomicReference<User> longAtomicReference = new AtomicReference<>();
            User oldUser = new User();
            oldUser.setName("old");
            longAtomicReference.set(oldUser);

            User userBak = oldUser; // Copy user addr
            oldUser = new User();

            boolean b = longAtomicReference.compareAndSet(oldUser, userBak); // Check addr value, if equals, update it
            System.out.println(b);
        }
    }

AtomicReferenceFieldUpdater

Modify object field

    public class AtomicReferenceFieldUpdaterTest {
    
        @Data
        private static class User {
            volatile String name;
        }

        public static void main(String[] args) {
            AtomicReferenceFieldUpdater<User, String> atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "name");
            User user = new User();
            user.setName("Wars");

            atomicReferenceFieldUpdater.compareAndSet(user, user.getName(), "Cat");
            System.out.println(user.getName());
        }
    }

AtomicStampedReference

AtomicStampedReference vs. AtomicReference added one version number param, fix ABA bug

    public class AtomicStampedReferenceTest {

        private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(0, 0);

        public static void main(String[] args) {
            atomicStampedReference.compareAndSet(0, 1, 0, 1);
        }
    }

AtomicLongArray

AtomicLongArray through the index operation array

    public class AtomicLongArrayTest {

        private static AtomicLongArray atomicLongArray = new AtomicLongArray(new long[]{1L, 2L, 3L});
        
        public static void main(String[] args) {
            atomicLongArray.incrementAndGet(0); // index
        }
    }

Synchronized

  • Code scope

    • If synchronized(this), not unique: The current instance is valid
    • If synchronized(A.class), unique: The all instance is valid
  • Function

    • equals synchronized(this){} this function content
    • The current instance is valid
  • Static function

    • equals synchronized(This.class){} this function content
    • The all instance is valid
  • Class

    • equals synchronized(This.class){} all function content of this class
    • The all instance is valid

volatile

Guaranteed single thread, sequential execution of reads and writes
1) Wrong usage

    @NotThreadSafe
    public class VolatileTest {

        private static final int MAX_THREAD = 50;
        private static final int EXECUTE_COUNT = 5000;
        private static volatile int count = 0;

        public static void main(String[] args) {
            CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            // Executed out
            new Thread(() -> {
                try {
                    countDownLatch.await();
                    System.out.println("count: " + count);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();

            // Executor
            ExecutorService executor = Executors.newCachedThreadPool();
            Semaphore semaphore = new Semaphore(MAX_THREAD);
            for (int i = 0; i < EXECUTE_COUNT; i++) {
                executor.execute(() -> {
                    try {
                        semaphore.acquire();
                        count++;
                        semaphore.release();
                        countDownLatch.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }

2) Recommended usage

    public class VolatileTest2 {

        private static volatile boolean semaphore = false;

        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                try {
                    while (!semaphore) Thread.sleep(1000);
                    System.out.println("Finish");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();


            Thread.sleep(10000);
            semaphore = true;
        }
    }

Singleton publish

  1. Dual if lazy mode
    @ThreadSafe
    public class A_Singleton {

        private volatile static A_Singleton instance;

        private A_Singleton() {
        }

        public static A_Singleton getInstance() {
            if (null == instance)
                synchronized (A_Singleton.class) {
                    instance = new A_Singleton(); // If no volatile, it may lead to Out-of-order execution initialization
                }

            return instance;
        }
    }
  1. Static scope Hungry mode
    @ThreadSafe
    public class B {

        private static B instance;

        static {
            instance = new B();
        }

        private B() {
        }

        public static B getInstance() {
            return instance;
        }
    }
  1. Enum mode
    enum E {
        INSTANCE;

        private C_EnumMode instance;

        E() {
            instance = new C_EnumMode();
        }

        public C_EnumMode getInstance() {
            return instance;
        }
    }

缓存一致性

CPU的读写单元正常情况下不能直接访问内存,因为并没有管脚直接连接到内存。相反,CPU和一级缓存通讯,一级和二级,二级和三级...n级才能和内存通讯。

如只处理Read,事情会变很简单,因为

所有级别的缓存遵循:在任意时刻,任意级别缓存中的内容等同于它对应的内存中的内容.

如果考虑写操作

直写(遵循定律)

我们透过本级缓存,直接将数据写到下一级缓存,如当前对应段已被这级缓存了,则更新/丢弃。

回写

缓存不会立即吧写操作传递,仅把本级已修改段标记为脏段。脏段会触发回写(写入下一级), 脏段洗净。总结: 当一个脏段被丢弃时,总会先回写。
回写定律: 所有脏段回写后, 任意级别缓存中的内容等同于它对应的内存中的内容.

一致性协议

当单核情况时,没毛病。
如果有多个核, 且拥有自己的缓存(有些CPU共用一块缓存[太慢了]), 且为了解决回写带来的问题, 所以产生了MESI及其衍生协议.

  • Modified 已修改缓存段,当某个核心持有了M状态的缓存段, 其他拥有或等待拥有其内存段的其他核心中的拷贝会马上失效(I)
  • Exclusive 独占缓存段,与主存一致拷贝, 当某个核心持有了E状态的缓存段,其他拥有有其内存段的其他核心中的拷贝会马上失效(I), 且其他核心无法同时持有它
  • Shared 共享缓存段, 与主存一致拷贝, 此状态下的缓存段是只读的, 可被多个核心持有
  • Invalid 失效缓存段, 属于脏段,已不在缓存中/被其他核心M/E, 将会被忽略(相当于从来没被加载)

MESI 定律:在所有的脏缓存段(M 状态)被回写后,任意缓存级别的所有缓存段中的内容,和它们对应的内存中的内容一致。此外,在任意时刻,当某个位置的内存被一个处理器加载入独占缓存段时(E 状态),那它就不会再出现在其他任何处理器的缓存中。

有很多文章说“CPU乱序执行和预测执行”会导致最终写入到内存的数据不符合预期,事实是此结果会被丢弃。
参考:
CPU乱序执行和预测执行导致的安全问题
Wikipedia: Out-of-order execution

代换法解(Substitution method)upper bounds

  1. Guess the form of the solution.
  2. Verify by induction.
  3. Solve for constants.
    Example: T(n) = 4T(n/2) + n
Inductive hypothesis: T(k) ≤ c1k2 – c2k for k < n.
         T(n) = 4T(n/2) + n
             = 4(c1(n/2)2 – c2(n/2)) + n
             = c1n2 – 2c2n + n
             = c1n2 – c2n – (c2n – n)
             ≤ c1n2 – c2n if c2 ≥ 1.

递归树法(Recursion-tree method)解upper bounds

Example: T(n) = T(n/4) + T(n/2) + n²

              = n² + T(n/4) + T(n/2)
              = n² + (n/4)² + (n/2)² + T(n/16)² + T(n/8)² + T(n/8)² + T(n/4)²
              = ...
              = n²(1 + 5/16 + (5/16)² + (5/16)³ + ...)
              = Θ(n²) // ignore consts

主方法(Master method)解upper bounds

T(n) = aT(n/b) + f(n), where a>=1, b>1, and f is asymptotically positive.
case1:

Compare f(n) with nlogba:
    f(n) = O(n^(logba–ε)) for some constant ε > 0.
    • f(n) grows polynomially slower than nlogba(by an nε factor).
    => T(n) = Θ(n^(logba)).

case2:

    f(n) = Θ(n^(logba)*lgn) for some constant k ≥ 0.
    • f(n) and n(logba) grow at similar rates.
    => T(n) = Θ(n^(logba) lgn).

case3:

    f(n) = Ω(n^(logba)+ε) for some constant ε > 0.
    • f(n) grows polynomially faster than nlogba (by
    an nε factor),
    and f(n) satisfies the regularity condition that
    af(n/b) ≤ cf(n) for some constant c < 1.
    => T(n) = Θ(f(n)).

Example1: T(n) = 4T(n/2) + n
n^(logba–1) = n, case1, Result: Θ(n^(logba))

Example2: T(n) = 4T(n/2) + n²
n^(logba) = n², case2, Result: Θ(n^(logba) * lgn)

Example3: T(n) = 4T(n/2) + n³
n^(logba+1) = n³, case3, Result: Θ(n³)

ES6

1) 变量关键字

1.1) let

let实现了作用域

1.2) const

const实现了常量

2) 字符串扩展

2.1) includes()

是否include了传入str

2.2) startsWith()

传入str是否在头部

2.3) endsWith()

传入str是否在尾部

2.4) 模板标记

`构建字符串,中可任意换行

3) 解构表达式

3.1) Array解构[]

let arr = [1, 3, 5];
let [a,b] = arr;
console.log(a, b);

3.2) 对象解构{}

let obj = {name: "Wars", cat: "wcat"};
let {name, cat} = obj;
console.log(name, cat);
let {name:n, cat:c} = obj;
console.log(n, c);

4) 函数优化

4.1) 函数形参默认值

function func(a, b = 10) {
    return a + b;
}
console.log(func(1));

4.2) 箭头函数

// 等价于上
let func2 = (a, b=10) => a + b;
console.log(func2(1));
// 无形参
let func5 = () => console.log('func5')
// 单形参
let func3 = a => a + 10;
console.log(func3(1));
// 多行实现
let func4 = a => {
    return a + 10;
}
console.log(func4(1));

4.3) 对象嵌套函数简写以及箭头函数结合解构

let person = {
    name : "Wars",
    func1: function(){
        console.log(this.name, );
    },
    // 不能使用this
    func2 : () => console.log(person.name),
    // 忽略key
    func3(){
        console.log(this.name);
    }
}
// 结合解构
({name}) => {
    console.log(name);
}

5) Map和Reduce

5.1) Map

拿出Array中每个元素执行传入函数,将返回值产生一个新Array

    let arr3 = ['1', '2', '3'];
    let arr4 = arr3.map(s => parseInt(s));
    console.log(arr4);

5.2) Reduce

reduce(func(a, b), [initvar])
形参1接受一个函数,a为初始值,b为Array下一个参数,不定义
形参2时初始值为Array[0],反之b为Array[0],函数返回值充当
下一个a,遍历完所有元素reduce返回结果

    let arr5 = [1, 2, 3];
    let result0 = arr5.reduce((a, b) => a + b, 0);
    console.log(result0)

6) 扩展运算符

...array,将一个array转成一个参数序列

let func6 = (a, b) => a+b;
let arr7 = [1, 2];
console.log(func6(...arr7));
// 数组合并
let arr8 = [...arr7, 3];
console.log(arr8);
// 结合解构
let [ax, ...axs] = arr8;
console.log(ax, axs);
// 字符串转array
console.log([..."array"]);

7) Promise容器

可以理解为一个异步操作容器,并提供了回调用于外部访问

const promise = new Promise(function(resolve, reject) {
    // ..........
    if (true){
        resolve("OK");
    }else{
        reject("ERROR");
    }
});
// 获取成功回调
promise.then(function(value) {
    console.log(value)
}).catch(function(error) {
    console.log(error)
});

8) Set和Map

Map和Object的区别:map的key可以是任意类型,obj只能是str

// Set
let set = new Set();
set.add(1);    // 添加
// 使用Array初始化
set = new Set([1, 2, 3, 4]);
set.clear();    // 晴空
set.has(1);    // 是否存在
set.delete(1);    // 删除为1元素
set.forEach(func);// 遍历元素
set.size;    // 元素个数,非函数
// Map
let map = new Map([ // 构造函数接受嵌套数组
     ["k1", "v1"]
    ,["k2", "v2"]
]);
// 接收Set
map = new Map(
    new Set([
         ["k1", "v1"]
        ,["k3", "v3"]
    ])
);
map = map; // 接收map

map.clear();    // 清空
map.delete(key);// 删除
map.has(key);    // 是否存在
map.size;    // 元素个数,非函数
map.forEach((k,v) => {});    // 遍历
map.values();    // 获取Value迭代器
map.keys();    // 获取key迭代器
map.entries();    // 获取entries迭代器

9) Class

class Person{
    constructor(name, age = 18) {
        this.name = name;
        this.age = age;
    }

    getName(){
        return this.name;
    }

    static isAdult(age) {
        if(18 <= age) {
            return true;
        }
            return false;
    }
}

class User extends Person{
    constructor() {
        super("name", 16);
    }
}

10) Generator函数

异步编程解决方案
通过function* 定义,函数体使用yield定义内部状态

functoin* gen() {
    // before
    yield "before";
    // body
    yield "body";
    // after
    yield "after";
    return "done";
}

let g = gen();
console.log(g.next()); // value: before, done: false
console.log(g.next()); // value: body, done: false
console.log(g.next()); // value: after, done: false
console.log(g.next()); // value: done, done: true
console.log(g.next()); // value: undefined, done: true

11) Decorator修饰器

SE7引入Decorator是一个函数,用来修改类的行为

@T // 该类似于注解东西表示引用该类
class User{
    constructor() {
    }
}

function T(target) {
    target.name = "Wars";
}

11.1) 转码器

用于把高规范代码转成SE5代码

  • Babel
  • Traceur

当我们的Handler中请求参数使用一个Model时,SpringMVC会先调用空参构造反射创建Instance,这就会造成无论请求传参如何,这个Model始终不为Null,但是属性为Null。
不好进行非空判断,但是如果不做,浪费不必要的开销也不好,于是尝试改改MVC的源码,想用懒加载的思想延迟创建Instance,奈何创建Instance步骤的位置和赋值步骤的嵌套层级相隔数层,暂且放弃。
最后采用了这种方法,在Model中定义一个isValidAddress函数,至于反射-。-别了别了

    public boolean isValidAddress() {
        return !Stream.of(pageId, siteId, templateId, pageName, pageAlias)
                .allMatch(Objects::isNull);
    }