java保证线程安全的两种方式

来源:互联网 发布:郑州施工队 网络布线 编辑:程序博客网 时间:2024/06/05 10:53

        JVM有主内存(Main Memory)和工作内存(Working Memory),主内存就是平时所说的java堆内存,存放程序中所有的类实例、静态数据等变量,是线程共享的,而工作内存中存放的是从主内存中拷贝过来的变量以及访问方法所取得的局部变量,是每个线程独立所有的,其他线程不能访问。

每个线程都有自己的执行空间(即工作内存),线程执行的时候用到某变量,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作:读取,修改,赋值等,这些均在工作内存完成,操作完成后再将变量写回主内存;

各个线程都从主内存中获取数据,线程之间数据是不可见的;打个比方:主内存变量A原始值为1,线程1从主内存取出变量A,修改A的值为2,在线程1未将变量A写回主内存的时候,线程2拿到变量A的值仍然为1;

这便引出“可见性”的概念:当一个共享变量在多个线程的工作内存中都有副本时,如果一个线程修改了这个共享变量的副本值,那么其他线程应该能够看到这个被修改后的值,这就是多线程的可见性问题。

普通变量情况:如线程A修改了一个普通变量的值,然后向主内存进行写回,另外一条线程B在线程A回写完成了之后再从主内存进行读取操作,新变量的值才会对线程B可见;

如下图:


Java内存模型定义了8种操作来完成关于主内存和工作内存之间具体的交互,这些操作都是原子的,不可分割(long double类型除外)。这8种操作如下所示:

  • 1) lock(锁定) 作用于主内存的变量,它把一个变量标志为一条线程独占的状态
  • 2) unlock(解锁) 作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其它线程锁定
  • 3) read(读取) 作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • 4) load(载入) 作用于工作内存的变量,它把read操作从主内存得到的变量值放入工作内存的变量副本中
  • 5) use(使用) 作用于工作内存的变量,它把变量副本的值传递给执行引擎,每当虚拟机遇到一个需要使用的变量的值的字节码指令时,将会执行这个操作。
  • 6) assign(赋值) 作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作副本变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作
  • 7) store(存储) 作用于工作内存的变量,将工作副本变量的值传输给主内存,以便随后的write操作使用
  • 8) write(写入) 作用于主内存的变量, 它把store操作从工作内存得到的变量的值放入主内存的变量

  如果要把一个变量从主内存复制到工作内存,那就要按顺序地执行read和load操作,如果要把变量从工作内存同步回主内存,那就要顺序地执行store和write操作。注意,Java内存模型只要求上述两个操作必须按顺序地执行,而没有保证必须是连续执行,也就是说read和load之间,store和write之间是可以插入其它指令的,如对内存中的变量a,b进行访问时,一种可能出现的顺序是read a, read b, load b, load a。

一.最简单的方式是加入synchronized关键字,这会获取对象锁,其他线程函数将被一直阻塞直到该锁释放:

import static java.lang.System.out;public class Counting {public static void main(String[] args)throws InterruptedException {class Count{private int count=0;public <span style="color:#ff6666;">synchronized</span> void increment(){count++;}public int getCount(){return count;}}final Count counter=new Count();class CountingThread extends Thread {public void run(){for (int i=0; i<1000;i++ ) {counter.increment();}}}CountingThread t1=new CountingThread();CountingThread t2=new CountingThread();t1.start();t2.start();t1.join();t2.join();out.println(counter.getCount());}}

二.使用锁:

import static java.lang.System.out;import java.util.concurrent.locks.ReentrantLock;public class thread02 {public static int count=0;public static void main(String[] args) throws InterruptedException{ReentrantLock lock=new ReentrantLock();class My_thread01 extends Thread{public void run(){lock.lock();try{for(int i=0; i<10000;i++) {count++;}}finally{lock.unlock();}}}class My_thread02 extends Thread{public void run(){lock.lock();try{for(int i=0; i<10000;i++) {count++;}}finally{lock.unlock();}}}My_thread01 t1=new My_thread01();My_thread02 t2=new My_thread02();t1.start();t2.start();t1.join();t2.join();out.println("count is "+count);}}

区别:

a.Lock使用起来比较灵活,但需要手动释放和开启;

b.Lock只适用于与代码块锁,而synchronized对象之间是互斥!


0 0