java多线程入门(二)如何保证数据原子性

简介: java多线程入门(二)如何保证数据原子性

1.多线程有问题的例子



网络异常,图片无法展示
|


2.为了解决上面个的问题我们可以进行那些操作



2.1加锁


2.1.1锁 synchronized


Synchronized的升级顺序是 无锁–>偏向锁–>轻量级锁–>重量级锁,顺内不可逆

使用很简单写在非静态方法上锁的对象为this

写在静态方法中的时候锁的对象为当前的类


  • 同一个对象写在非静态方法上
  • 不同对象写在静态方法上
  • 每次获取到锁的线程执行完,才能让下个获取锁的线程执行,每次执行是同步的


网络异常,图片无法展示
|


写在代码块中注意锁的对象 synchronized(obj){}和方法的静态非静态一样


网络异常,图片无法展示
|


网络异常,图片无法展示
|


2.1.2锁 lock


lock锁获取和释放时手工的,所以用的时候注意在finally中释放锁


private static int money = 0;
  Lock lock=new ReentrantLock();
  @Override
  public void run() {
    for (int i = 0; i < 10000; i++) {
      lock.lock();
      money++;
      lock.unlock();
    }
    System.out.println("处理后金额:" + money);
  }
复制代码


lock锁的一些方法


方法名称 描述
void lock() 获得锁。如果锁不可用,则当前线程将被禁用以进行线程调度,并处于休眠状态,直到获取锁。
void lockInterruptibly() 获取锁,如果可用并立即返回。如果锁不可用,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,和lock()方法不同的是在锁的获取中可以中断当前线程(相应中断)。
Condition newCondition() 获取等待通知组件,该组件和当前的锁绑定,当前线程只有获得了锁,才能调用该组件的wait()方法,而调用后,当前线程将释放锁。
boolean tryLock() 只有在调用时才可以获得锁。如果可用,则获取锁定,并立即返回值为true;如果锁不可用,则此方法将立即返回值为false 。
boolean tryLock(long time, TimeUnit unit) 超时获取锁,当前线程在一下三种情况下会返回: 1. 当前线程在超时时间内获得了锁;2.当前线程在超时时间内被中断;3.超时时间结束,返回false.
void unlock() 释放锁。

lockInterruptibly()


通过这个方法获取锁的时候,是可以相应中断的。但是一旦获取了锁之后是不会相应中断的,因为interrupt()方法只能中断阻塞过程中的线程而不能中断正在运行过程中的线程。Synchronized只有获取到锁之后才能中断,等待锁时不可中断


2.1.3锁 ReadWriteLock锁


//返回用于读取操作的锁  
Lock readLock()   
//返回用于写入操作的锁  
Lock writeLock() 
ReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
lock.readLock().unlock();
lock.writeLock().lock();
lock.writeLock().unlock();
复制代码


ReentrantLock 是互斥锁。

ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作;多个度之间不是互斥的,但是写和读是互斥的,写合写之间是互斥的;只要没有 writer,读取锁可以由多个 reader 线程同时保持,而写入锁是独占的。


2.2 synchronized和lock的区别


  • synchronized是关键字当有异常抛出时系统会自动释放锁,
    lock是个接口 释放锁需要手动,需要放在finally{}
  • synchronized是非公平的,
    lock可以设置公平和非公平 默认是非公平的
  • synchronized和lock都是可重入锁(就是获取当前的锁,调用别的地方也需要锁这个锁的类型一致可直接获取锁操作)
  • synchronized响应中断的时候需要调方法 Thread.currentThread().isInterrupted()去判断,但是在阻   塞获取锁的时候是无法中断的
    lock的tryLock和lockInterruptibly可以相应中断的
  • synchronized中如果执行比较久的话,其他线程会一直在阻塞等待中;
    lock的话可以使用tryLock和lockInterruptibly 采取中断或者到时间后自动退出;
  • 在读写的时候synchronized是排他锁,每个请求都必须获取到锁;
    ReadWriteLock锁可以把读写分离出来,如果都是读的情况可以同时获取锁提高效率


2.3采用原子性变量


private AtomicBoolean isTrue;
private AtomicInteger num;
private AtomicLong    numLong;
private AtomicReference reference;
复制代码


2.2.1 AtomicReference


public class PeoperEntity {
  private String name;
  private String sex;
  public PeoperEntity(String name,String sex) {
    this.name=name;
    this.sex=sex;
  }
}
public static void main(String[] args) {
  PeoperEntity p1=new PeoperEntity("张三","1");
  PeoperEntity p2=new PeoperEntity("李四","2");
  PeoperEntity p3=new PeoperEntity("王五","3");
  AtomicReference<PeoperEntity> atomicReference = new AtomicReference<>();
  atomicReference.set(p1);
        //如果当前值是p1则修改为p2
  atomicReference.compareAndSet(p1, p2);
  System.out.println(atomicReference.get());
        //如果当前值是p1则修改为p3 因为p1变成了p2所以结果为p2
  atomicReference.compareAndSet(p1, p3);
  System.out.println(atomicReference.get());
  }
复制代码


PeoperEntity [name=李四, sex=2]

PeoperEntity [name=李四, sex=2]

相关文章
|
3天前
|
设计模式 前端开发 Java
【前端学java】SpringBootWeb极速入门-分层解耦(03)
【8月更文挑战第13天】SpringBootWeb极速入门-分层解耦(03)
8 2
【前端学java】SpringBootWeb极速入门-分层解耦(03)
|
4天前
|
开发框架 前端开发 Java
【前端学java】SpringBootWeb极速入门-实现一个简单的web页面01
【8月更文挑战第12天】SpringBootWeb极速入门-实现一个简单的web页面01
15 3
【前端学java】SpringBootWeb极速入门-实现一个简单的web页面01
|
4天前
|
JSON 前端开发 Java
【前端学java】SpringBootWeb极速入门-请求参数解析(02)
【8月更文挑战第12天】SpringBootWeb极速入门-请求参数解析(02)
10 1
【前端学java】SpringBootWeb极速入门-请求参数解析(02)
|
1天前
|
前端开发 IDE Java
"揭秘前端转Java的秘径:SpringBoot Web极速入门,掌握分层解耦艺术,让你的后端代码飞起来,你敢来挑战吗?"
【8月更文挑战第19天】面向前端开发者介绍Spring Boot后端开发,通过简化Spring应用搭建,快速实现Web应用。本文以创建“Hello World”应用为例,展示项目基本结构与运行方式。进而深入探讨三层架构(Controller、Service、DAO)下的分层解耦概念,通过员工信息管理示例,演示各层如何协作及依赖注入的使用,以此提升代码灵活性与可维护性。
|
4天前
|
存储 安全 Java
"Java编码魔法:揭秘图片与文件的Base64神秘转换术,让数据在指尖跳跃!"
【8月更文挑战第16天】Base64编码在Java开发中常用于将二进制数据如图片转换为ASCII字符串以便传输。编码使用64个字符及等号填充,每3字节数据编码为4个字符。Java利用`java.util.Base64`类实现此功能:读取图片或文件为字节数组后进行编码。解码时将Base64字符串还原为字节数组并写入文件。需注意编码效率降低、不提供安全性及特殊字符兼容性等问题。掌握这些技巧有助于解决Web开发中的数据传输需求。
17 4
|
3天前
|
存储 缓存 安全
深度剖析Java HashMap:源码分析、线程安全与最佳实践
深度剖析Java HashMap:源码分析、线程安全与最佳实践
|
5天前
|
缓存 前端开发 JavaScript
一篇文章助你搞懂java中的线程概念!纯干货,快收藏!
【8月更文挑战第11天】一篇文章助你搞懂java中的线程概念!纯干货,快收藏!
13 0
一篇文章助你搞懂java中的线程概念!纯干货,快收藏!
|
3天前
|
算法 安全 Java
深入解析Java多线程:源码级别的分析与实践
深入解析Java多线程:源码级别的分析与实践
|
4天前
|
存储 监控 算法
掌握Java内存管理:从入门到精通
在Java的世界里,内存管理是程序运行的心脏。本文将带你走进Java内存管理的奥秘,从基础概念到高级技巧,一步步揭示如何优化你的Java应用。准备好迎接挑战,让我们共同揭开高效内存使用的面纱!
|
5天前
|
NoSQL Redis
Lettuce的特性和内部实现问题之在同步调用模式下,业务线程是如何拿到结果数据的
Lettuce的特性和内部实现问题之在同步调用模式下,业务线程是如何拿到结果数据的