[Idea of Java Programming] Synchronization and Collaborative Work Classes

Read-write lock ReentrantReadWriteLock

Two kinds of locks were introduced before: synchronized and explicit lock ReentrantLock, for access to the same protected object, whether read or write, they all require the same lock. However, in some scenarios, the read operations of multiple threads can be completely parallelized.

In the Java concurrent package, the interface ReadWriteLock represents the read-write lock, and the main implementation class is the reentrant read-write lock ReentrantReadWriteLock.

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}

Two locks are generated through a ReadWirteLock: a read lock and a write lock. Read operations use read locks, and write operations use write locks. "Read-Read" operations can be parallelized, "Read-Write", "Write-Write" operations cannot

Only one thread can perform a write operation. When acquiring a write lock, it can only be acquired if no thread holds any lock. When holding a write lock, no other thread can acquire any lock.
Multiple threads can acquire and hold read locks without other threads holding write locks.

Internally, they use the same integer variable to represent the lock status, 16 bits for read locks and 16 bits for write locks. One variable is used to facilitate CAS operations, and there is actually only one lock waiting queue.

Semaphore

The locks introduced earlier restrict that only one thread can access a resource at the same time.
In some cases, a single resource can be accessed concurrently, but a large number of concurrent accesses may affect performance, so it is desirable to limit the number of concurrently accessed threads.

Semaphore Semaphore can limit the number of concurrent access to resources

public Semaphore(int permits)
public Semplhore(int permits, boolean fair)

fair indicates whether it is fair, permits indicate the number of licenses

There are two main methods: acquire permission and release permission

// 阻塞获取许可,可响应中断
public void acquire() throws InterruptedException;
// 阻塞获取许可,不响应中断
public void acquireUninterruptibly();
// 批量获取多个许可
public void acquire(int permits) throws InterruptedException;
public void acquireUninterruptibly(int permits);
// 尝试获取
public boolean tryAcquire();
// 限定等待时间获取
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException;

// 释放许可
public void release();

The basic principle of semaphore is relatively simple. Based on AQS, permits represent the number of shared locks. The acquire method is to check whether the number of locks is greater than 0. If it is greater than 1, the acquisition is successful. Otherwise, wait. Release is the number of locks. Add one to wake up the first waiting thread.

CountDownLatch

CountDownLatch is equivalent to a door latch. It is closed at the beginning. All threads that want to pass the door need to wait, and then start the countdown. After the countdown becomes 0, the latch is opened and all threads waiting can pass. It is one-time . Once opened, it cannot be closed again.

Two application scenarios of CountDownLatch:

Threads start at the same time

public class RacerWithCountDownLatch {
    static class Racer extends Thread {
        CountDownLatch latch;
        public Racer(CountDownLatch latch) {
            this.latch = latch;
        }
        @Override
        public void run() {
            try {
                // 检查计数是否为0,如果大于0就等待,await可以被中断
                this.latch.await();
                System.out.println(getName() + " start Run ");
            } catch (InterruptedException e) {
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        int num = 10;
        CountDownLatch countDownLatch = new CountDownLatch(1);
        Thread[] racers = new Thread[num];
        for (int i = 0; i < racers.length; i++) {
            racers[i] = new Racer(countDownLatch);
            racers[i].start();
        }
        Thread.sleep(2000);
        // 检查计数,如果已经为0,直接返回;否则减少计数。 如果新的计数变为0,则唤醒所有等待的线程
        countDownLatch.countDown();
    }
}

master-slave collaboration

public class MasterWorkerDemo {
    static class Worker extends Thread {
        CountDownLatch latch;
        public Worker(CountDownLatch latch) {
            this.latch = latch;
        }
        @Override
        public void run() {
            try {
                Thread.sleep((long) (Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                this.latch.countDown();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        int workerNum = 100;
        CountDownLatch countDownLatch = new CountDownLatch(workerNum);
        Worker[] worker = new Worker[workerNum];
        for (int i = 0; i < worker.length; i++) {
            worker[i] = new Worker(countDownLatch);
            worker[i].start();
        }
        System.out.println("Main wait");
        countDownLatch.await();
        System.out.println("Main run");
    }
}

CyclicBarrier

CyclicBarrier is equivalent to a fence. All threads need to wait for other threads after reaching the fence. After all threads arrive, they pass together. It is circular and can be used for repeated synchronization.

// 表示参与的线程个数
public CyclicBarrier(int parties)
// 接口一个Runnable参数,当所有线程到达栅栏后,在所有线程执行下一步动作前,运行参数中的动作,这个动作由最后一个到达栅栏的线程执行
public CyclicBarrier(int parties, Runnable barrierAction)

// 表示自己已经到达,如果自己是最后一个到达的,就执行可选的命令,执行后,唤醒所有等待的线程,最后重置内部的同步计数器。
public int await() throw InterruptedException, BrokenBarrierException;
// 可以限定最长等待时间
public int await() throw InterruptedException, BrokenBarrierException, TimeoutException;

In CyclicBarrier, the participating threads affect each other. As long as another thread is interrupted when calling await, or times out, the barrier will be destroyed. If the fence action throws an exception, the fence will also be destroyed. After being destroyed, all threads calling await will exit, throwing a BrokenBarrierException

The difference between CyclicBarrier and CountDownLatch:
1. CyclicBarrier can be reused, and CountDownLatch is one-time.
2. CyclicBarrier has the same thread role, which is used for coordination between threads of the same role, and everyone waits for each other. The participating threads of CountDownLatch are different roles, some are responsible for the countdown, and some are waiting.
3. CountDownLatch counts down, while CyclicBarrier counts up

ThreadLocal

Just see how to use it

public class ThreadLocalDemo {
    static ThreadLocal<Integer> local = new ThreadLocal<Integer>();
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(){
            @Override
            public void run() {
                System.out.println("child thread init : " + local.get());
                local.set(998);
                System.out.println("child thread final : " + local.get());
            }
        };
        local.set(10);
        System.out.println("main thread:" + local.get());
        thread.start();
        thread.join();
        System.out.println("main thread final :" + local.get());
    }
}

local is a static variable, the main method creates a sub-thread, and both the main thread and the sub-thread access local. The output is as follows:

main thread:10
child thread init : null
child thread final : 998
main thread final :10

It means that although they access the same variable local, each thread has its own independent value, which is the role of ThreadLocal.

In addition to the get/set method, there are two commonly used methods

// 用于提供初始值,当调用get方法时,如果之前没有设置过,会调用该方法获取初始值,默认返回null
protected T initialValue();
// 删除当前线程对应的值
public void remove();

ThreadLocal principle

public ThreadLocal() {
}

public void set(T value) {
    Thread t = Thread.currentThread();
    // 获取ThreadLcalMap,ThreadLcalMap是一个内部类,与一般的map不同,它的键类型为WeakReference<ThreadLocal>,利于回收内存
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // 在线程自己的ThreadLcalMap中设置一个条目,键为当前的ThreadLocal对象,值是value
        map.set(this, value);
    else
        // 创建ThreadLcalMap并存入值
        createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}


public T get() {
    Thread t = Thread.currentThread();
    // 获取ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 调用setInitialValue
    return setInitialValue();
}

private T setInitialValue() {
    // 调用initialValue获取值
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326273187&siteId=291194637