Java线程并发干扰问题及解决方式(锁对象)

并发干扰

使用多线程开发可以很好地提高代码效率,但是在多线程对同一数据资源的共享中,许多线程需要的同一个数据做读写修改操作,因此必然会存在竞争问题,而且这些问题通常会带来灾难性的后果,我们来看一个例子:

现在有一个多线程的银行账户管理系统,我的账户内有余额 1000 元,现在有两个线程对我的账户进行操作:
线程 A :存入500元
线程 B :取出200元

我们应该知道,在大多服务器操作系统中都采用抢占式调度(线程状态及属性),每个线程只有一个时间片来执行任务,当时间片用完后会立刻暂停运行交由其他线程,然后再重新排队

在这种情况下,线程 A 和线程 B,可能就会产生冲突:

我们会发现,到最后我的账户只剩了800元,完全忽略了存钱的操作,这肯定不行!

锁对象

为解决代码块并发访问干扰,有两种机制:一种是 Java 提供的 synchronized 关键字,另一种是 ReentrantLock 类(唯一实现了 Lock 接口的对象)。我们来看看利用ReentrantLock 类如何解决这一问题

ReentrantLock

用 ReentrantLock 保护代码块的基本结构:

myLock.lock(); // 一个 ReentrantLock 对象
try{
    ...
}
finally{
    myLock.unlock(); // 确保即使出现异常,也能解锁
}

这种方法理解起来其实很简单,我们可以把 lock 到 unlock 之间的区域想象成一个封锁区,任何一个线程一旦调用 lock,那么在它 unlock 之前, 其他任何线程都不能进入这段封锁区
如果有这么一个锁对象来保护我的代码,那么之前的问题就会变成这样:

这样一来,多线程共享资源就安全多了

我们要注意,在这里的锁对象只是针对我的账户的对象,不同的账户会有自己不同的锁对象,而线程操作不同的账户时,线程之间互不影响

还要注意,锁对象可以嵌套使用,线程可以重复获得已经持有的锁,锁会保持一个持有计数,用来跟踪 lock 方法的嵌套调用,线程每调用一次 lock 方法都还要调用 unlock 方法来解锁

ReentrantLock( ):构建一个可以被用来保护临界区的可重入的锁
ReentrantLock( boolean fair ):构建一个带有公平策略的锁,一个公平锁会偏向于等待时间长的线程,但是这一公平机制会大大降低性能,所以,默认情况下,锁没有被强制为公平的

ReentrantReadWriteLock(读/写锁)

Lock readLock( ) : 得到一个可以被多个读操作共用的读锁,但会排斥所有写操作

Lock writeLock( ):得到一个写锁,排斥所有其他读操作和写操作

使用 ReentrantReadWriteLock 必要步骤:

private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

private Lock readLock = rwl.readLock();
private Lock readLock = rwl.writeLock();

// 对所有读取方法加读锁
public double getTotalBalance() {
    readLock.lock();
    try {...}
    finally { readLock.unlock(); }
}

// 对所有修改方法加写锁
public void transfer(...) {
    writeLock.lock();
    try {...}
    finally { writeLock.unlock(); }
}
时间: 2024-10-16 01:08:24

Java线程并发干扰问题及解决方式(锁对象)的相关文章

Java线程并发中常见的锁--自旋锁 偏向锁

  随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制.     1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作.也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式.在JVM中使用-XX:+UseBiasedLocki

Java中高精度问题的bigdecimal解决方式

最近有空写了点老的J2EE的代码,发现有一个十分有意思的问题,当用Hibernate从数据库里把浮点数读取出来的时候做一些比如累加的工作,例如 summary 或者递减之类的,就会发现在最后的结果中会出现些许问题. 如:3.41+5.2+56.2+23.3+... (这类两位小数的价钱),结果会出现103.00000000000001这种结果,但是人算的话反而会得出正常的数据.看样子double,float这类数据精度上来了还会有这类问题.     于是,翻了点资料,在有的编程语言中提供了专门的

浅谈Java线程并发知识点_java

发布:一个对象是使它能够被当前范围之外的代码所引用: 常见形式:将对象的的引用存储到公共静态域:非私有方法中返回引用:发布内部类实例,包含引用. 逃逸:在对象尚未准备好时就将其发布. 不要让this引用在构造函数中逸出.例,在构造函数中启动线程,线程会包含对象的引用. 同步容器:对容器的所有状态进行穿行访问,Vector.Hashtable,Cllections.synchronizedMap|List 并发容器:ConcurrentHashMap,CopyOnWriteArrayList,Co

java线程并发countdownlatch类使用示例_java

复制代码 代码如下: package com.yao; import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; /** * CountDownLatch是个计数器,它有一个初始数, * 等待这个计数器的线程必须等到计数器倒数到零时才可继续. */public class CountDownLatchTe

java线程并发cyclicbarrier类使用示例_java

复制代码 代码如下: package com.yao; import java.util.Random;import java.util.concurrent.CyclicBarrier; /** * CyclicBarrier类似于CountDownLatch也是个计数器, * 不同的是CyclicBarrier数的是调用了CyclicBarrier.await()进入等待的线程数, * 当线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续. * Cy

java线程并发semaphore类示例_java

复制代码 代码如下: package com.yao; import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore; /** * Java 5.0里新加了4个协调线程间进程的同步装置,它们分别是: * Semaphore, CountDownLatch, CyclicBarrier和Exchanger. * 本例主要介

java线程并发blockingqueue类使用示例_java

如果BlockingQueue是满的任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有新的空间才会被唤醒继续操作. BlockingQueue提供的方法主要有: add(anObject): 把anObject加到BlockingQueue里,如果BlockingQueue可以容纳返回true,否则抛出IllegalStateException异常. offer(anObject):把anObject加到BlockingQueue里,如果BlockingQueue

一段Java线程并发代码的修改

问题描述 本代码用于测试Tomcat服务器压力要求在原有的基础上做出如下改进:1.当连接返回的时间超过5S则认为连接失败,结束测试,返回失败结果.2.如果可以的话最好编写一个简单的图形化界面,用于输入线程数,显示返回结果等.package com.eteda.test;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReade

保持java线程间可见性的5种方式

Primitive Writes up to and including- ...are made visible to- Object the end of a synchronized block or method a thread entering a synchronized block or method for the same object. Volatile field a write to a volatile field any thread reading that vo