• ThreadLocal,Lock的事儿


    ThreadLocal作用 防止线程间的干扰

    public interface Sequence {
    
        int getNumber();
    }
    
    public class ClientThread extends Thread {
    
        private Sequence sequence;
    
        public ClientThread(Sequence sequence) {
            this.sequence = sequence;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + " => " + sequence.getNumber());
            }
        }
    }
    
    
    public class SequenceA implements Sequence {
    
        private static int number = 0;
    
        public int getNumber() {
            number = number + 1;
            return number;
        }
    
        public static void main(String[] args) {
            Sequence sequence = new SequenceA();
    
            ClientThread thread1 = new ClientThread(sequence);
            ClientThread thread2 = new ClientThread(sequence);
            ClientThread thread3 = new ClientThread(sequence);
    
            thread1.start();
            thread2.start();
            thread3.start();
        }
    }
    
    Thread-0 => 1
    Thread-0 => 2
    Thread-0 => 3
    Thread-2 => 4
    Thread-2 => 5
    Thread-2 => 6
    Thread-1 => 7
    Thread-1 => 8
    Thread-1 => 9
    线程之间共享了 static 变量
    线程干扰

    使用ThreadLocal当作容器

    public class SequenceB implements Sequence {
    
        private static ThreadLocal<Integer> numberContainer = new ThreadLocal<Integer>() {
            @Override
            protected Integer initialValue() {
                return 0;
            }
        };
    
        public int getNumber() {
            numberContainer.set(numberContainer.get() + 1);
            return numberContainer.get();
        }
    
        public static void main(String[] args) {
            Sequence sequence = new SequenceB();
    
            ClientThread thread1 = new ClientThread(sequence);
            ClientThread thread2 = new ClientThread(sequence);
            ClientThread thread3 = new ClientThread(sequence);
    
            thread1.start();
            thread2.start();
            thread3.start();
        }
    }
    这样就不共享了
    使用ThreadLocal

    ThreadLocal原理

        public T get() {
            Thread t = Thread.currentThread();
            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;
                }
            }
            return setInitialValue();
        }
    
        private T setInitialValue() {
            T value = initialValue();
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
            return value;
        }
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    
    就是以当前线程为键创建了map

    含有事务时,可以把 Connection 放到了 ThreadLocal 中,将每个线程的connection隔开

    Lock 的事儿

    遇到一个文件可以多人同时读,但不能同时写

    public class Data {
    
        private final char[] buffer;
    
        public Data(int size) {
            this.buffer = new char[size];
            for (int i = 0; i < size; i++) {
                buffer[i] = '*';
            }
        }
    
        public String read() {
            StringBuilder result = new StringBuilder();
            for (char c : buffer) {
                result.append(c);
            }
            sleep(100);
            return result.toString();
        }
    
        public void write(char c) {
            for (int i = 0; i < buffer.length; i++) {
                buffer[i] = c;
                sleep(100);
            }
        }
    
        private void sleep(long ms) {
            try {
                Thread.sleep(ms);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    读写文件
    Read Thread
    public class WriterThread extends Thread {
    
        private final Data data;
        private final String str;
        private int index = 0;
    
        public WriterThread(Data data, String str) {
            this.data = data;
            this.str = str;
        }
    
        @Override
        public void run() {
            while (true) {
                char c = next();
                data.write(c);
            }
        }
    
        private char next() {
            char c = str.charAt(index);
            index++;
            if (index >= str.length()) {
                index = 0;
            }
            return c;
        }
    }
    Write Thread

    资源的访问一定要做到“共享互斥”

    public class Data {
    
        ...
    
        public synchronized String read() {
            ...
        }
    
        public synchronized void write(char c) {
            ...
        }
    
        ...
    }
    加锁

    加锁后性能慢 , 自己创建锁

    public class ReadWriteLock {
    
        private int readThreadCounter = 0;      // 正在读取的线程数(0个或多个)
        private int waitingWriteCounter = 0;    // 等待写入的线程数(0个或多个)
        private int writeThreadCounter = 0;     // 正在写入的线程数(0个或1个)
        private boolean writeFlag = true;       // 是否对写入优先(默认为是)
    
        // 读取加锁
        public synchronized void readLock() throws InterruptedException {
            // 若存在正在写入的线程,或当写入优先时存在等待写入的线程,则将当前线程设置为等待状态
            while (writeThreadCounter > 0 || (writeFlag && waitingWriteCounter > 0)) {
                wait();
            }
            // 使正在读取的线程数加一
            readThreadCounter++;
        }
    
        // 读取解锁
        public synchronized void readUnlock() {
            // 使正在读取的线程数减一
            readThreadCounter--;
            // 读取结束,对写入优先
            writeFlag = true;
            // 通知所有处于 wait 状态的线程
            notifyAll();
        }
    
        // 写入加锁
        public synchronized void writeLock() throws InterruptedException {
            // 使等待写入的线程数加一
            waitingWriteCounter++;
            try {
                // 若存在正在读取的线程,或存在正在写入的线程,则将当前线程设置为等待状态
                while (readThreadCounter > 0 || writeThreadCounter > 0) {
                    wait();
                }
            } finally {
                // 使等待写入的线程数减一
                waitingWriteCounter--;
            }
            // 使正在写入的线程数加一
            writeThreadCounter++;
        }
    
        // 写入解锁
        public synchronized void writeUnlock() {
            // 使正在写入的线程数减一
            writeThreadCounter--;
            // 写入结束,对读取优先
            writeFlag = false;
            // 通知所有处于等待状态的线程
            notifyAll();
        }
    }
    ReadWriteLock

    jdk已经提供了这种锁

    public interface Lock {
    
        void lock();
    
        void lockInterruptibly() throws InterruptedException;
    
        boolean tryLock();
    
        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
        void unlock();
    
        Condition newCondition();
    }
    Lock
    public class Data {
    
        ...
    
        private final ReadWriteLock lock = new ReentrantReadWriteLock(); // 创建读写锁
        private final Lock readLock = lock.readLock();    // 获取读锁
        private final Lock writeLock = lock.writeLock();  // 获取写锁
    
        ...
    
        public String read() throws InterruptedException {
            readLock.lock(); // 读取上锁
            try {
                return doRead(); // 执行读取操作
            } finally {
                readLock.unlock(); // 读取解锁
            }
        }
    
        public void write(char c) throws InterruptedException {
            writeLock.lock(); // 写入上锁
            try {
                doWrite(c); // 执行写入操作
            } finally {
                writeLock.unlock(); // 写入解锁
            }
        }
    
        ...
    }
    加锁后的操作data

    当系统中出现不同的读写线程同时访问某一资源时,需要考虑共享互斥问题,可使用 synchronized 解决次问题。若对性能要求较高的情况下,可考虑使用 ReadWriteLock 接口及其 ReentrantReadWriteLock 实现类,当然,自己实现一个 ReadWriteLock 也是一种解决方案。此外,为了在高并发情况下获取较高的吞吐率,建议使用 Lock 接口及其 ReentrantLock 实现类来替换以前的 synchronized 方法或代码块。

  • 相关阅读:
    “字节跳动-文远知行杯”广东工业大学第十四届程序设计竞赛
    Codeforces Beta Round 77 (Div. 2 Only)
    Codeforces Round 263(Div. 2)
    Codeforces Round 262 (Div. 2)
    《Introduction to Algorithm》-chaper30-多项式与快速傅里叶变换
    算法专题-STL篇
    算法专题-暴力枚举篇
    初等数论及其应用——中国剩余定理
    初等数论及其应用——费马小定理
    《A First Course in Probability》-chape4-离散型随机变量-几种典型分布列
  • 原文地址:https://www.cnblogs.com/mxz1994/p/8205595.html
Copyright © 2020-2023  润新知