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]

相关文章
|
2天前
|
数据采集 JSON Java
Java爬虫获取微店快递费用item_fee API接口数据实现
本文介绍如何使用Java开发爬虫程序,通过微店API接口获取商品快递费用(item_fee)数据。主要内容包括:微店API接口的使用方法、Java爬虫技术背景、需求分析和技术选型。具体实现步骤为:发送HTTP请求获取数据、解析JSON格式的响应并提取快递费用信息,最后将结果存储到本地文件中。文中还提供了完整的代码示例,并提醒开发者注意授权令牌、接口频率限制及数据合法性等问题。
|
3天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
39 14
|
6天前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
36 13
|
7天前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
17天前
|
存储 NoSQL Java
使用Java和Spring Data构建数据访问层
本文介绍了如何使用 Java 和 Spring Data 构建数据访问层的完整过程。通过创建实体类、存储库接口、服务类和控制器类,实现了对数据库的基本操作。这种方法不仅简化了数据访问层的开发,还提高了代码的可维护性和可读性。通过合理使用 Spring Data 提供的功能,可以大幅提升开发效率。
60 21
|
1月前
|
存储 分布式计算 Hadoop
基于Java的Hadoop文件处理系统:高效分布式数据解析与存储
本文介绍了如何借鉴Hadoop的设计思想,使用Java实现其核心功能MapReduce,解决海量数据处理问题。通过类比图书馆管理系统,详细解释了Hadoop的两大组件:HDFS(分布式文件系统)和MapReduce(分布式计算模型)。具体实现了单词统计任务,并扩展支持CSV和JSON格式的数据解析。为了提升性能,引入了Combiner减少中间数据传输,以及自定义Partitioner解决数据倾斜问题。最后总结了Hadoop在大数据处理中的重要性,鼓励Java开发者学习Hadoop以拓展技术边界。
50 7
|
29天前
|
SQL Java 数据库连接
【潜意识Java】深入理解MyBatis的Mapper层,以及让数据访问更高效的详细分析
深入理解MyBatis的Mapper层,以及让数据访问更高效的详细分析
60 1
|
1月前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
Java C语言 C++
Java 的数据类型划分(数据类型划分)| 学习笔记
快速学习 Java 的数据类型划分(数据类型划分)
139 0
Java 的数据类型划分(数据类型划分)| 学习笔记
|
Java 开发者 Windows
Java 数据类型划分(字符型)|学习笔记
快速学习 Java 数据类型划分(字符型)
143 0