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,Lock的事儿的更多相关文章

  1. ThreadLocal 那点事儿

    原文出处: 黄勇 ThreadLocal,直译为“线程本地”或“本地线程”,如果你真的这么认为,那就错了!其实,它就是一个容器,用于存放线程的局部变量,我认为应该叫做 ThreadLocalVaria ...

  2. ThreadLocal 那点事儿(续集)

    本篇是<ThreadLocal 那点事儿>的续集,如果您没看上一篇,就就有点亏了.如果您错过了这一篇,那亏得就更大了. 还是保持我一贯的 Style,用一个 Demo 来说话吧.用户提出一 ...

  3. ThreadLocal使用场景案例

    本篇是<ThreadLocal 那点事儿>的续集,如果您没看上一篇,就就有点亏了.如果您错过了这一篇,那亏得就更大了. 还是保持我一贯的 Style,用一个 Demo 来说话吧.用户提出一 ...

  4. 架构探险——第二章(为web应用添加业务功能)

    第二章不使用框架完成了自己的Web应用. 重点: 服务层的完善优化过程,思路 在看这一段的时候引起了无数次的共鸣.相信大家在开始接触Java Web的时候,都做过类似的封装和优化. 第一版 在Serv ...

  5. 关于 SimpleDateFormat 的非线程安全问题及其解决方案

    一直以来都是直接用SimpleDateFormat开发的,没想着考虑线程安全的问题,特记录下来(摘抄的): 1.问题: 先来看一段可能引起错误的代码: package test.date; impor ...

  6. Java_基础_02_ThreadLocal

    二.参考资料 1.ThreadLocal 那点事儿 2.彻底理解ThreadLocal

  7. Java并发编程:synchronized、Lock、ReentrantLock以及ReadWriteLock的那些事儿

    目录 前言 synchronized用法 修饰方法 修饰实例方法 修饰静态方法 同步代码块 引出Lock Lock用法 子类:ReentrantLock 读写分离锁:ReadWriteLock Loc ...

  8. Synchronized、lock、volatile、ThreadLocal、原子性总结、Condition

    http://blog.csdn.net/sinat_29621543/article/details/78065062

  9. 多线程同步工具——Lock

    本文原创,转载请注明出处. 参考文章: <"JUC锁"03之 公平锁(一)> <"JUC锁"03之 公平锁(二)> 锁分独占锁与共享锁, ...

随机推荐

  1. 清空echarts的option

    将相应的echarts的option治为空 $("#tt5sbmc").html("");

  2. JAVA基础知识|反射

    一.理解反射 1.1.基础概念 反射:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为ja ...

  3. JAVA基础知识|小知识点

    1.强烈建议,不使用char类型 那么,到底为什么java里不推荐使用char类型呢?其实,1个java的char字符并不完全等于一个unicode的字符.char采用的UCS-2编码,是一种淘汰的U ...

  4. 修改oracle用户登录密码

    运行sqlplus进入输入密码界面 用户名输入: connect as sysdba 密码:这边乱输就可以了 然后进行输入下面的命令: 修改密码命令 alter user system identif ...

  5. JS获取div高度的方法

    有时在写页面时,需要获取一个div的高度.怎么才能获取呢?哈哈,先上结论.有两种方法. offsetHeight .clientHeight getComputedStyle offsetHeight ...

  6. 网络文件共享服务—NFS服务

    NFS服务 NFS:Network File System 网络文件系统,基于内核的文件系统: Sun公司开发,通过使用NFS,用户和程序可以像访问本地文件一样访问远端系统上的文件,基于RPC(Rem ...

  7. 解决python在命令行中运行时导入包失败,出现错误信息 "ModuleNotFoundError: No module named ***"

    转自https://www.cnblogs.com/dreamyu/p/7889959.html https://www.cnblogs.com/lifeofershisui/p/8135702.ht ...

  8. Mysql -- 设置指定配置文件启动

    mysqld --defaults-file=/etc/my.cnf --user=root

  9. linux的最简socket编程

    一.背景 好久没有进行linux下的socket编程了,复习一下 二.服务端完整代码 #include <stdio.h> #include <stdlib.h> #inclu ...

  10. OpenStack Cinder发展动态系列--Austin峰会

    在Mitaka版本,Cinder团队在多个特性和领域取得了重大进展. 本文将做一个简要的介绍:关于在Mitaka版本已经完成的功能和特性,以及讨论在Newton版本将会开发的功能和特性. 1 Cind ...