ThreadLocal的事儿

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

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

时间: 2024-08-02 12:04:35

ThreadLocal的事儿的相关文章

ThreadLocal 那点事儿(续集)

还是保持我一贯的 Style,用一个 Demo 来说话吧.用户提出一个需求:当修改产品价格的时候,需要记录操作日志,什么时候做了什么事情. 想必这个案例,只要是做过应用系统的小伙伴们,都应该遇到过吧?无外乎数据库里就两张表:product 与 log,用两条 SQL 语句应该可以解决问题: ? 1 2 update product set price = ? where id = ? insert into log (created, description) values (?, ?) But

ThreadLocal 那点事儿

ThreadLocal,直译为"线程本地"或"本地线程",如果你真的这么认为,那就错了!其实,它就是一个容器,用于存放线程的局部变量,我认为应该叫做 ThreadLocalVariable(线程局部变量)才对,真不理解为什么当初 Sun 公司的工程师这样命名. 早在 JDK 1.2 的时代,java.lang.ThreadLocal 就诞生了,它是为了解决多线程并发问题而设计的,只不过设计得有些难用,所以至今没有得到广泛使用.其实它还是挺有用的,不相信的话,我们一起

弄明白ThreadLocal类

1.ThreadLocal类的由来 因为有问题,人类就会想法设法的创造一些东西出来解决问题,嗯,这句话同意吧. 假如目前有这么一个问题:有个家庭,三个孩子都想看妈妈买的一本童话书,但是只有一本书,该如何是好? 方法一:家里没钱买第二本了,那就排队看,谁跑得快来到妈妈面前的就先看.后面来晚的,候着等着.等前面的看完再到你.于是 以时间换空间的synchronized 类出现了. 方法二:多大的事儿,你们爸爸有钱,随便任性.立马再买两本一模一样的,人手一本.于是以空间换时间的ThreadLocal类

关于ThreadLocal 的用法

ThreadLocal,直译为"线程本地"或"本地线程",如果你真的这么认为,那就错了!其实,它就是一个容器,用于存放线程的局部变量,我认为应该叫做 ThreadLocalVariable(线程局部变量)才对,真不理解为什么当初 Sun 公司的工程师这样命名. 早在 JDK 1.2 的时代,java.lang.ThreadLocal 就诞生了,它是为了解决多线程并发问题而设计的,只不过设计得有些难用,所以至今没有得到广泛使用.其实它还是挺有用的,不相信的话,我们一起

ThreadLocal来管理事务

ThreadLocal (扩展) 1 ThreadLocal API ThreadLocal类只有三个方法: l  void set(T value):保存值: l  T get():获取值: l  void remove():移除值. 2 ThreadLocal的内部是Map ThreadLocal内部其实是个Map来保存数据.虽然在使用ThreadLocal时只给出了值,没有给出键,其实它内部使用了当前线程做为键. class MyThreadLocal<T> { private Map&

线程的私家小院儿:ThreadLocal

转载自simplemain老王的公众号 话说在<操作系统原理>这门课里面,我们学到了很多概念:进程.线程.锁.PV操作.读写者问题等等,其中关于进程.线程和锁的东西是我们平时工作中用到最多的:服务器接收到用户请求,需要用一个进程或者一个线程去处理,然后操作内存.文件或者数据库的时候,可能需要对他们进行加锁操作. 不过作为一个有追求的程序员,我们有些时候会不满足于此 .于是,我们开始对线程.锁开始了漫漫的优化之路.其中有一种情况是非常值得优化的:假定我们现在有一个web服务,我们的程序一般都会为

详细领悟ThreadLocal变量

关于对ThreadLocal变量的理解,我今天查看一下午的博客,自己也写了demo来测试来看自己的理解到底是不是那么回事.从看到博客引出不解,到仔细查看ThreadLocal源码(JDK1.8),我觉得我很有必要记录下来我这大半天的收获,今天我研究的最多的就是这两篇文章说理解.我在这里暂称为A文章和B文章.以下是两篇博文地址,我是在看完A文章后,很有疑问,特别是在A文章后的各位网页的评论中,更加坚定我要弄清楚ThreadLocal到底是怎么一回事.A文章:http://blog.csdn.net

ThreadLocal 工作原理、部分源码分析

1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocalMap 静态内部类维护了一个Entry 数组 private Entry[] table; 查看Entry 源码,它维护了两个属性,ThreadLocal 对象 与一个Object static class Entry extends W

有些事儿,工程师可能今生仅此一次

郑昀 创建于2016/9/15 最后更新于2016/9/18 关键词:深度思考,碎片化阅读,做论文,深入研究, 早先在<技术高手如何炼成>一文中提到,我会问面试者,你日常如何构建自己的知识体系.有人会觉得你怎么就问出这么宏大的问题?知识体系,这是什么鬼? 面试时的交谈 工作之后你做过这样的事情吗? 面试是一个谁主张谁举证的过程,有时候需要面试者举出实例,自我证明. 而我认为问一些我们工作中遇到的难题和业务场景是在“欺负”面试者,所以我喜欢问开放型问题: 在你工作之后,你有没有像做毕业论文一样对