Thread安全
目录:
- 隐式锁,又称线程同步synchronized
- 显式锁Lock 和 ReentrantLock
- 显式锁ReadWriteLock 和 ReentrantReadWriteLock
- 显式锁 StampedLock
- Java 关键字 volatile 修饰变量
- 原子操作: atomic
- JAVA单例模式(线程安全,高并发性能又高)
- Java 线程池
- Future任务机制: Fork/Join 框架
- 同步计数器 CountDownLatch
ReentrantLock 、ReentrantReadWriteLock 、StampedLock 都是代码块层面的锁定,要保证锁定一定会被释放,就必须将unlock()放到finally{}中。
Java 作为平台无关性语言,JLS(Java语言规范)定义了统一的内存管理模型JMM(Java Memory Model),JMM屏蔽了底层平台内存管理细节,在多线程环境中必须解决可见性和有序性的问题。
JMM规定了jvm有主内存(Main Memory)和工作内存(Working Memory):
(1)主内存其实就是我们平时所说的Java堆内存
存放程序中的所有类实例,静态数据等变量。是多个线程共享的。
(2)工作内存
存放的是该线程从主内存中拷贝过来的变量以及访问方法所取得的局部变量。是每个线程私有的,其它线程不能访问。
线程在引用变量时不能直接从主内存中引用,如果线程工作内存中没有该变量,则从主内存中拷贝一个副本到工作内存中。(这个过程叫read-load)
线程不安全:
单个用户干活类:
1 public class Count { 2 3 public int number = 0; 4 public void add() { 5 try { 6 7 Thread.sleep(51); // mock user do something 8 } catch (InterruptedException ex) { 9 ex.printStackTrace(); 10 } 11 number += 1; 12 System.out.printf(Thread.currentThread().getName() + "-" + number); 13 14 } 15 16 }
用户类,干count的活
1 public class TheadA extends Thread { 2 3 private Count mCount; 4 5 public ThreadA(Count count) { 6 mCount = count; 7 } 8 9 public void run() { 10 mCount.add(); 11 } 12 }
5个人干完活,最后的值是:
1 public class ThreadMain { 2 3 public static void main(String[] args) { 4 5 Count count new Count(); 6 for (int i = 0; i < 5; i++) { 7 8 ThreadA task = new ThreadA(count); 9 task.start(); 10 } 11 12 try { 13 14 Thread.sleep(1001); // wait 5 users do something 15 } catch (InterruptedException ex) { 16 ex.printStackTrace(); 17 } 18 19 System.out.printf("5个人干完活, 最后的值是" + count.number); 20 } 21 }
运行结果:(多线程,不安全问题,每次运行结果不一样)
1 Thread-1-1 2 Thread-0-1 3 Thread-3-3 4 Thread-4-2 5 Thread-2-3 6 5个人干完活, 最后的值是:3
为了安全,改下Count,添加 synchronized 关键字:
1 public class Count { 2 3 public int number = 0; 4 5 public synchronized void add() { 6 try { 7 8 Thread.sleep(51); // mock user do something 9 } catch (InterruptedException ex) { 10 ex.printStackTrace(); 11 } 12 number += 1; 13 System.out.printf(Thread.currentThread().getName() + "-" + number); 14 15 } 16 17 }
运行结果:(每次运行结果都一样,这就是线程安全,保证数据的高度一致性和准确性)
1 Thread-0-1 2 Thread-4-2 3 Thread-3-3 4 Thread-2-4 5 Thread-1-5 6 5个人干完活, 最后的值是:5
实现Thead安全大致有三种方法:
(1)多实例,也就是不用单例模式了。
(2)使用java.util.concurrent下面的类库。
(3)使用锁机制synchronized、lock方式。