栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java的线程安全

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java的线程安全

线程安全是什么?什么条件下的东西是线程安全的?不安全会带来什么后果?

  • 线程安全是怎么一回事?
  • 为什么会有这样的一些线程安全的问题?

比如现在有个线程A 线程B 变量X =1 Y=1,AB同时读取 x为1 y为1 后续 A把x改为5 B把Y改为6 但是这个时候 A以为y还是为1 后续在做业务处理的就会产生计算错误!

多个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。 不进行恰当的控制,会导致线程安全问题。

根据并发性质来分析一下:

  1. 原子性:对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。

  2. 可见性: 对于可见性,Java提供了volatile关键字来保证可见性

    当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。另外,通过synchronized和Lock 也能够保证可见性synchronized 和 Lock 能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。

3.有序性: Java 允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执 行,却会影响到多线程并发执行的正确性。可以通过 volatile 关键字来保证一定的“有序 性”(synchronized 和Lock也可以)。

  • 那些手段可以解决这些线程安全的问题?
    • 多个线程过来 one by one 的处理 这种效率最高 但是中间会有脏数据 线程不安全

    • 或者是串行化

      以上两点肯定是不可行的

      在这个两点直接 正好是我们要解决的问题!

      示例代码:1

      public class Counter {
          //多线程计算问题
          private int sum = 0;
          private Object lock = new Object();
      ​
          public void incr() {
      //        synchronized(lock) {
                  sum = sum + 1;
      //        }
          }
          public int getSum() {
              return sum;
          }
          
          public static void main(String[] args) throws InterruptedException {
              int loop = 10_0000;
              
              // test single thread
              Counter counter = new Counter();
              for (int i = 0; i < loop; i++) {
                  counter.incr();
              }
      ​
              System.out.println("single thread: " + counter.getSum());
          
              // test multiple threads
              final Counter counter2 = new Counter();
              Thread t1 = new Thread(() -> {
                  for (int i = 0; i < loop / 2; i++) {
                      counter2.incr();
                  }
              });
              Thread t2 = new Thread(() -> {
                  for (int i = 0; i < loop / 2; i++) {
                      counter2.incr();
                  }
              });
              t1.start();
              t2.start();
              Thread.sleep(1000);
      ​
              System.out.println("multiple threads: " + counter2.getSum());
          
          
          }
      }

      输出的结果可以看到这是不安全的

      single thread: 100000 //单线程下可以正常计算 multiple threads: 70909 //多线程下就不是安全的

      示例代码 2

      package java0.conc0301.sync;
      ​
      public class Counter {
          
          private int sum = 0;
          private Object lock = new Object();
          //加入同步代码块
          public synchronized void incr() {
      //        synchronized(lock) {
                  sum = sum + 1;
      //        }
          }
          public int getSum() {
              return sum;
          }
          
          public static void main(String[] args) throws InterruptedException {
              int loop = 10_0000;
              
              // test single thread
              Counter counter = new Counter();
              for (int i = 0; i < loop; i++) {
                  counter.incr();
              }
      ​
              System.out.println("single thread: " + counter.getSum());
          
              // test multiple threads
              final Counter counter2 = new Counter();
              Thread t1 = new Thread(() -> {
                  for (int i = 0; i < loop / 2; i++) {
                      counter2.incr();
                  }
              });
              Thread t2 = new Thread(() -> {
                  for (int i = 0; i < loop / 2; i++) {
                      counter2.incr();
                  }
              });
              t1.start();
              t2.start();
              Thread.sleep(1000);
      ​
              System.out.println("multiple threads: " + counter2.getSum());
          
          
          }
      }

加入同步代码块之后 ,相当于排队一个一个的来 类似于数据库的串行化 但是这样无非就是分成了两个线程 效率又会变得跟单线程一样

输出的结果:

single thread: 100000 multiple threads: 100000

synchronized 的实现(同步代码块)

锁的几种状态:

 

 

  1. 偏向锁:就是该线程占有的锁 还是该线程来做使用的话 会更快一点 因为内存中有了锁的结构 指针。 比如一个人干的很久了,再让他干这个工作会更块一点!

  2. 轻量级锁:对synchronized 的优化 ,先不悲观的去操作这个锁 而是采用一种Cas方式去获取这个锁,如果发现这个锁被其他的线程占用,再去排队获取这个锁 给他设置为重量级的锁:(乐观锁机制)

  3. 重量级锁:原本之前的jdk版本 直接给标识 为已占有的标识 (开销比较大一点)

可以放在方法上 可以放在对象上

区别:锁的粒度不同

比如咱们这个业务里面 10行代码 三行需要做同步执行,其他行就可以不做锁 做--锁分离--的操作 目的减少 锁的粒度!

比如代码示例 3:

package java0.conc0301.sync;
​
public class Counter {
    
    private int sum = 0;
    private Object lock = new Object();
    //加入同步代码块 
    public  void incr() {
        synchronized(lock) {
            sum = sum + 1;
        }
    }
    public int getSum() {
        return sum;
    }
    
    public static void main(String[] args) throws InterruptedException {
        int loop = 10_0000;
        
        // test single thread
        Counter counter = new Counter();
        for (int i = 0; i < loop; i++) {
            counter.incr();
        }
​
        System.out.println("single thread: " + counter.getSum());
    
        // test multiple threads
        final Counter counter2 = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < loop / 2; i++) {
                counter2.incr();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < loop / 2; i++) {
                counter2.incr();
            }
        });
        t1.start();
        t2.start();
        Thread.sleep(1000);
​
        System.out.println("multiple threads: " + counter2.getSum());
    
    
    }
}

 

再描述一下volatile关键字

  1. 每次读取数据都强制从主内存中获取

  2. 使用的场景:单个线程写,多个线程读

  3. 原则:能不用就不用

  4. 替代的方案:Atomic原子操作类

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/849944.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号