并发编程专题四 - 原子操作和显示锁,java 面试刷题
自旋还是很消耗性能的
3、只能保证一个共享变量的原子操作
2.3、Jdk 中相关原子操作类的使用
更新基本类型类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
更新数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
更新引用类型:AtomicReference,AtomicMarkableReference,AtomicStampedReference
原子更新字段类: AtomicReferenceFieldUpdater,AtomicIntegerFieldUpdater,AtomicLongFieldUpdater (较少使用)
代码示例:
import java.util.concurrent.atomic.AtomicStampedReference;
/**
@Auther: BlackKingW
@Date: 2019/4/15 22:09
@Description:带版本号的原子操作
*/
public class UseAtomicStampedReference {
static AtomicStampedReference<String> asr =
new AtomicStampedReference<>("BlackKingW",0);
public static void main(String[] args) throws InterruptedException {
final int oldStamp = asr.getStamp();//那初始的版本号
final String oldReferenc = asr.getReference();
System.out.println(oldReferenc+"==========="+oldStamp);
Thread rightStampThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+"当前变量值:"+oldReferenc+"当前版本戳:"+oldStamp+"-"
+asr.compareAndSet(oldReferenc, oldReferenc+"Java",
oldStamp, oldStamp+1));
}
});
Thread errorStampThread = new Thread(new Runnable() {
@Override
public void run() {
String reference = asr.getReference();
System.out.println(Thread.currentThread().getName()
+"当前变量值:"+reference+"当前版本戳:"+asr.getStamp()+"-"
+asr.compareAndSet(reference, reference+"C",
oldStamp, oldStamp+1));
}
});
rightStampThread.start();
rightStampThread.join();
errorStampThread.start();
errorStampThread.join();
System.out.println(asr.getReference()+"==========="+asr.getStamp());
}
}
二、显式锁
=========
1、Lock****接口和核心方法
lock()? ?用来获取锁。如果锁已被其他线程获取,则进行等待。
unlock()?释放锁
tryLock()?它表示用来尝试获取锁,如果获取成功,则返回 true,如果获取失败(即锁已被其他线程获取
Lock 接口和 synchronized 的比较
synchronized:是 Java 语言内置关键字,不需要手动释放锁。代码简洁,
Lock:是实现的一个类,需要手动释放锁。并且获取锁可以被中断,拥有超时获取锁,尝试获取锁等机制。
代码示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
@Auther: BlackKingW
@Date: 2019/4/14 12:09
@Description:
*/
public class LockDemo {
private Lock lock = new ReentrantLock();
private int count;
public void increament() {
lock.lock();
try {
count++;
}finally {
lock.unlock();
}
}
public synchronized void incr2() {
count++;
incr2();
}
}
如 increament 采用 lock,代码相对复杂,并且使用 lock 一定要在 finally?中释放锁,否则可能会永远都释放不了,导致死锁。
2、可重入锁 ReentrantLock
可重入意思为:已经获得该锁的线程,可以再次进入被锁定的代码块。内部通过计数器实现。例如上面的代码
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
@Auther: BlackKingW
@Date: 2019/4/15 22:09
@Description:
*/
public class ReentrantLockDemo {
private Lock lock = new ReentrantLock();
private int count;
public void increament() {
lock.lock();
try {
count++;
}finally {
lock.unlock();
}
}
public synchronized void incr2() {
count++;
incr2();
}
public synchronized void test3() {
incr2();
}
}
在增加一个方法 test3,去调用 incr2,如果该锁不可以被重入,则无法调用 incr2。导致程序一直运行不下去。可重入锁就是支持已经获取锁的线程,可以重复进入加锁的代码块。
3、公平锁和非公平锁。
公平锁和非公平锁。何谓公平性,是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求上的绝对时间顺序,满足 FIFO
当多个线程去请求加锁代码块时,同时只能有一个线程拥有锁,那么其他线程如果是按照到来的先后顺序,那么这个锁就是公平锁。比如 ReentrantLock 可指定是否为公平和非公平锁。否则就是非公平锁。比如 synchronized。
公平锁 VS 非公平锁
公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象。
公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。因此,ReentrantLock 默认选择的是非公平锁,则是为了减少一部分上下文切换,保证了系统更大的吞吐量。
4、ReadWriteLock****接口和读写锁 ReentrantReadWriteLock
那是不是所有的锁都只能被一个线程所拥有呢?当然不是。例如 ReentrantReadWriteLock 读写锁。
ReentrantReadWriteLock 允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。
ReadWriteLock 接口有两个方法
Lock readLock();? 获取读锁
Lock writeLock();? 获取写锁
ReentrantReadWriteLock 实现了 ReadWriteLock 接口。用于获取读写锁。
ReentrantLock 和 synchronized 关键字,同时只能有一个线程持有,所以都是排他锁,而 ReentrantReadWriteLock 可以同时有多个线程去访问,这种所也叫共享锁。
使用场景: 读多写少的情况
代码示例
/**
@Auther: BlackKingW
@Date: 2019/4/15 22:09
@Description:
*/
public class UseSyn implements GoodsService {
private GoodsInfo goodsInfo;
public UseSyn(GoodsInfo goodsInfo) {
this.goodsInfo = goodsInfo;
}
@Override
public synchronized GoodsInfo getNum() {
SleepTools.ms(5);
return this.goodsInfo;
}
@Override
public synchronized void setNum(int number) {
SleepTools.ms(5);
goodsInfo.changeNumber(number);
}
}
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
@Auther: BlackKingW
@Date: 2019/4/15 22:09
@Description:
*/
public class UseRwLock implements GoodsService {
private GoodsInfo goodsInfo;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock getLock = lock.readLock();//读锁
private final Lock setLock = lock.writeLock();//写锁
public UseRwLock(GoodsInfo goodsInfo) {
this.goodsInfo = goodsInfo;
}
@Override
public GoodsInfo getNum() {
getLock.lock();
try {
SleepTools.ms(5);
return this.goodsInfo;
}finally {
getLock.unlock();
}
}
@Override
public void setNum(int number) {
setLock.lock();
try {
SleepTools.ms(5);
goodsInfo.changeNumber(number);
}finally {
setLock.unlock();
}
}
}
/**
@Auther: BlackKingW
@Date: 2019/4/14 12:09
@Description:
*/
public interface GoodsService {
public GoodsInfo getNum();//获得商品的信息
public void setNum(int number);//设置商品的数量
}
/**
@Auther: BlackKingW
@Date: 2019/4/15 22:09
@Description:
*/
public class GoodsInfo {
private final String name;
private double totalMoney;//总销售额
private int storeNumber;//库存数
public GoodsInfo(String name, int totalMoney, int storeNumber) {
this.name = name;
this.totalMoney = totalMoney;
this.storeNumber = storeNumber;
}
public double getTotalMoney() {
return totalMoney;
}
public int getStoreNumber() {
return storeNumber;
}
public void changeNumber(int sellNumber){
this.totalMoney += sellNumber*25;
this.storeNumber -= sellNumber;
}
}
import java.util.Random;
import java.util.concurrent.CountDownLatch;
/**
@Auther: BlackKingW
@Date:2019/4/15 22:09
@Description:
*/
public class BusiApp {
static final int readWriteRatio = 10;//读写线程的比例
static final int minthreadCount = 3;//最少线程数
//static CountDownLatch latch= new CountDownLatch(1);
//读操作
private static class GetThread implements Runnable{
private GoodsService goodsService;
public GetThread(GoodsService goodsService) {
this.goodsService = goodsService;
}
@Override
public void run() {
// try {
// latch.await();//让读写线程同时运行
// } catch (InterruptedException e) {
// }
long start = System.currentTimeMillis();
for(int i=0;i<100;i++){//操作 100 次
goodsService.getNum();
}
System.out.println(Thread.currentThread().getName()+"读取商品数据耗时:"
+(System.currentTimeMillis()-start)+"ms");
}
}
//写操做
private static class SetThread implements Runnable{
private GoodsService goodsService;
public SetThread(GoodsService goodsService) {
this.goodsService = goodsService;
}
@Override
public void run() {
// try {
// latch.await();//让读写线程同时运行
// } catch (InterruptedException e) {
// }
long start = System.currentTimeMillis();
评论