【JAVA】_001_多线程之线程安全


1.认识线程安全

怎么产生的线程安全?

  • 多个线程共享同一全局变量,做写操作时,如不做其他干预,可能会受其他线程干扰,导致数据错乱。

怎么解决线程安全?

  • 既然是多个线程对同一资源做写操作,那我们只要保证当A线程操作时B线程不能参与进来即可,JAVA中一般采用同步机制解决。

1.1.线程安全三大特性

1.1.1.原子性:

  • 原子性是指操作不可分割,表现在对于共享变量的操作应该是不可分的,必须连续完成

    如:a++操作,实际上JVM会分三步完成

    1.读取a的值

    2.a值+1

    3.将值赋予变量a

    • 期间任何一个操作异常都可能导致a值被篡改,出现线程安全问题。
  • 如果业务中有需要变量++的操作,岂不是都需加锁保证原子性?在JAVA1.8中有如下原子类

    jdk1.5中推荐使用AutomicInteger原子类,java1.8推荐使用LongAdder

    原子类底层采用CAS无锁机制,天生免疫死锁。

1.1.2.有限性:

  • 有序性是指程序在执行的时候,程序的代码执行顺序和语句的顺序是一致的。

    疑问:那为什么会出现不一致现象呢?

    • 由于重排序的缘故,执行程序时,为提高性能,编译器和处理器会对指令进行重排序
    • 重排序不会影响单线程的执行结果,但是在并发情况下,可能会出现诡异的BUG。

1.1.3.可见性:

  • 可见性是一线程对共享变量的修改,另一个线程能够立即看到。

    JAVA内存模型(JMM):JVM为了提高程序的执行效率,会对我们的程序进行优化,把经常需要被访问的变量存储在我们的缓存当中,也就是CPU中的寄存器(Cache)里,而避免直接去内存里读。多线程操作数据时,分为主内存和私有内存,如下图

1.2.锁机制(JVM锁)

synchronized 关键字

  • 用法:可在代码块和方法函数中加上改关键字。
  • 静态同步函数使用的是当前字节码文件,而非静态同步函数是this锁。
  • 线程执行成功后或执行过程中发生异常都会自动释放锁
  • 在保证线程安全的同时会导致其它线程的阻塞

缺点:效率较低,扩展性不高,不能自定义。

lock锁

  • 用法:ReentrantLock lock= new ReentrantLock();
    • 使用lock.lock();上锁,使用lock.unlock();释放锁。
    • 1、线程取锁失败后会进入等待状态,超过指定时间后会直接返回false,而不会像synchronized一样阻塞其它线程
      2、程序执行完毕或者出现异常时需要手动释放锁,否则会出现死锁
      3、可中断锁
      4、默认采用非公平锁,根据需求可以设置成公平锁,而synchronized只能是非公平锁
      公平锁:线程取锁失败后会进入等待队列,先进入队列的线程会先获得锁
      非公平锁:线程取锁失败后会进入等待队列,但等待线程取锁的概率是随机的

1.3.总结

  • 了解线程安全是什么

  • 了解原子类

  • 了解synchronized 关键字

  • 了解lock锁


文章作者: truly
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 truly !
  目录