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原理
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; } }
资源的访问一定要做到“共享互斥”
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(); } }
jdk已经提供了这种锁
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
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(); // 写入解锁 } } ... }
当系统中出现不同的读写线程同时访问某一资源时,需要考虑共享互斥问题,可使用 synchronized 解决次问题。若对性能要求较高的情况下,可考虑使用 ReadWriteLock 接口及其 ReentrantReadWriteLock 实现类,当然,自己实现一个 ReadWriteLock 也是一种解决方案。此外,为了在高并发情况下获取较高的吞吐率,建议使用 Lock 接口及其 ReentrantLock 实现类来替换以前的 synchronized 方法或代码块。