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]

相关文章
|
1月前
|
自然语言处理 Java
Java中的字符集编码入门-增补字符(转载)
本文探讨Java对Unicode的支持及其发展历程。文章详细解析了Unicode字符集的结构,包括基本多语言面(BMP)和增补字符的表示方法,以及UTF-16编码中surrogate pair的使用。同时介绍了代码点和代码单元的概念,并解释了UTF-8的编码规则及其兼容性。
101 60
|
13天前
|
SQL Java 数据库连接
【潜意识Java】深入理解MyBatis的Mapper层,以及让数据访问更高效的详细分析
深入理解MyBatis的Mapper层,以及让数据访问更高效的详细分析
28 1
|
17天前
|
存储 分布式计算 Hadoop
基于Java的Hadoop文件处理系统:高效分布式数据解析与存储
本文介绍了如何借鉴Hadoop的设计思想,使用Java实现其核心功能MapReduce,解决海量数据处理问题。通过类比图书馆管理系统,详细解释了Hadoop的两大组件:HDFS(分布式文件系统)和MapReduce(分布式计算模型)。具体实现了单词统计任务,并扩展支持CSV和JSON格式的数据解析。为了提升性能,引入了Combiner减少中间数据传输,以及自定义Partitioner解决数据倾斜问题。最后总结了Hadoop在大数据处理中的重要性,鼓励Java开发者学习Hadoop以拓展技术边界。
38 7
|
27天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
85 17
|
2月前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
1月前
|
存储 Java BI
java怎么统计每个项目下的每个类别的数据
通过本文,我们详细介绍了如何在Java中统计每个项目下的每个类别的数据,包括数据模型设计、数据存储和统计方法。通过定义 `Category`和 `Project`类,并使用 `ProjectManager`类进行管理,可以轻松实现项目和类别的数据统计。希望本文能够帮助您理解和实现类似的统计需求。
80 17
|
23天前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
2月前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
2月前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
70 1
|
4月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
77 1