码迷,mamicode.com
首页 > 其他好文 > 详细

ThreadLocal的事儿

时间:2018-01-05 17:28:38      阅读:125      评论:0      收藏:0      [点我收藏+]

标签:boolean   lock   close   防止   write   ecb   extends   number   情况下   

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 方法或代码块。

 

ThreadLocal的事儿

标签:boolean   lock   close   防止   write   ecb   extends   number   情况下   

原文地址:https://www.cnblogs.com/mxz1994/p/8205595.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!