Lock接口以及核心方法
- lock
- tryLock
- unlock
使用范式如下
public class LockDemo {
private Lock lcok = new ReentrantLock();
private int count = 0;
private void increament() {
lcok.lock();
try {
count++;
} finally {
lcok.unlock();
}
}
public synchronized void incr2() {
count++;
incr2();
}
public synchronized void test3() {
incr2();
}
}
Lock与synchronized关键字比较
- synchronized
- 代码简洁,非必须使用Lock场景建议使用
- Lock
- 获取锁时可用中断
- 可以超时获取锁
- 可以尝试获取锁
- ReadWriteLock
- 读多些少的场景
公平锁于非公平锁
-
ReentrantLock是可重入锁,即当线程释放锁之后,该线程仍然可以获取到刚刚释放的锁,常用在递归
-
公平锁于非公平锁:在时间上,先对锁进行获取请求的一定先获得锁,一般非公平锁的效率要高一些
-
ReentrantLock有两个构造方法如下,默认为公平锁。
public ReentrantLock() {sync = new NonfairSync();} public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync();}
ReadWriteLock 接口和读写锁ReentrantReadWriteLock
ReentrantLock与synchronized 都是排他锁,ReentrantReadWriteLock中的写锁为排他锁,读锁为共享锁。
读写锁:同一时刻允许多个读线程访问,但是在写线程访问的时候,此时所有的读线程与写线程都会被阻塞,使用与读多写少的情况
在ReentrantReadWriteLock本身并没有实现Lock,但是定义了两把锁,分别实现了Lock,如下:
/** Inner class providing readlock */
private final ReentrantReadWriteLock.ReadLock readerLock;
/** Inner class providing writelock */
private final ReentrantReadWriteLock.WriteLock writerLock;
在读多写少的情况下使用读些锁于synchronized比较
先写一个GoodsInfo类
package com.sakura.ch4.rw;
public class GoodsInfo {
private final String name;
private int number;
private double money;
public GoodsInfo(String name, int number, double money) {
this.name = name;
this.number = number;
this.money = money;
}
public int getNumber() {
return number;
}
public double getMoney() {
return money;
}
public void change(int count) {
this.money += count * 25;
this.number -= count;
}
}
定义一个操作接口GoodsService
public interface GoodsService {
public GoodsInfo getNum();
public void setNum(int count);
}
使用syhchronized
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 count) {
SleepTools.ms(5);
this.goodsInfo.change(count);
}
}
使用读些锁
public class UseRW implements GoodsService {
private GoodsInfo goodsInfo;
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock readLock = readWriteLock.readLock();
private final Lock writeLock = readWriteLock.writeLock();
public UseRW(GoodsInfo goodsInfo) {
this.goodsInfo = goodsInfo;
}
@Override
public GoodsInfo getNum() {
readLock.lock();
try {
SleepTools.ms(5);
return this.goodsInfo;
} finally {
readLock.unlock();
}
}
@Override
public void setNum(int count) {
writeLock.lock();
try {
SleepTools.ms(5);
goodsInfo.change(count);
} finally {
writeLock.unlock();
}
}
}
编写测试类
public class Test {
static final int ReadWriteRatio = 10;
static final int minWriteThread = 3;
static CountDownLatch latch = new CountDownLatch(1);
private static class ReadThread implements Runnable {
private GoodsService goodsService;
public ReadThread(GoodsService goodsService) {
this.goodsService = goodsService;
}
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long start = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
goodsService.getNum();
}
System.out.println(Thread.currentThread().getName() + " 获取数据花费" + (System.currentTimeMillis() - start) + "ms");
}
}
private static class WriteThread implements Runnable {
private GoodsService goodsService;
public WriteThread(GoodsService goodsService) {
this.goodsService = goodsService;
}
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long start = System.currentTimeMillis();
Random r = new Random();
for (int i = 0; i < 10; i++) {
// SleepTools.ms(50);
goodsService.setNum(r.nextInt(10));
}
System.out.println(Thread.currentThread().getName() + " 写数据花费" + (System.currentTimeMillis() - start) + "ms");
}
}
public static void main(String[] args) {
// 1⃣️ GoodsInfo sam = new GoodsInfo("sam", 10000, 100000);
// 2⃣️ GoodsService goodsService = new UseSyn(sam);
GoodsService goodsService = new UseRW(sam);
for (int i = 0; i < minWriteThread; i++) {
Thread writeThread = new Thread(new WriteThread(goodsService));
for (int j = 0; j < ReadWriteRatio; j++) {
Thread readThread = new Thread(new ReadThread(goodsService));
readThread.start();
}
SleepTools.ms(100);
writeThread.start();
}
latch.countDown();
}
}
若运行1⃣️,输出
Thread-27 获取数据花费1799ms
Thread-26 获取数据花费2402ms
Thread-25 获取数据花费3007ms
Thread-21 获取数据花费3960ms
Thread-19 获取数据花费4793ms
Thread-18 获取数据花费5406ms
Thread-17 获取数据花费6012ms
Thread-16 获取数据花费6637ms
Thread-14 获取数据花费7490ms
Thread-9 获取数据花费8359ms
Thread-7 获取数据花费8969ms
Thread-6 获取数据花费9818ms
Thread-5 获取数据花费10430ms
Thread-2 获取数据花费11811ms
Thread-3 获取数据花费11961ms
Thread-4 获取数据花费12260ms
Thread-10 获取数据花费12628ms
Thread-8 获取数据花费13218ms
Thread-13 获取数据花费13799ms
Thread-15 获取数据花费14181ms
Thread-20 获取数据花费14558ms
Thread-28 获取数据花费15240ms
Thread-30 获取数据花费15539ms
Thread-31 获取数据花费16090ms
Thread-32 获取数据花费16688ms
Thread-1 获取数据花费16910ms
Thread-24 获取数据花费17328ms
Thread-0 写数据花费17360ms
Thread-11 写数据花费17377ms
Thread-23 获取数据花费17598ms
Thread-12 获取数据花费17992ms
Thread-29 获取数据花费18313ms
Thread-22 写数据花费18321ms
若运行2⃣️输出
Thread-22 写数据花费66ms
Thread-0 写数据花费126ms
Thread-11 写数据花费192ms
Thread-9 获取数据花费769ms
Thread-3 获取数据花费769ms
Thread-15 获取数据花费769ms
Thread-6 获取数据花费769ms
Thread-5 获取数据花费769ms
Thread-21 获取数据花费768ms
Thread-10 获取数据花费769ms
Thread-1 获取数据花费769ms
Thread-8 获取数据花费769ms
Thread-17 获取数据花费768ms
Thread-4 获取数据花费769ms
Thread-2 获取数据花费769ms
Thread-14 获取数据花费769ms
Thread-7 获取数据花费769ms
Thread-13 获取数据花费769ms
Thread-19 获取数据花费774ms
Thread-16 获取数据花费774ms
Thread-32 获取数据花费774ms
Thread-25 获取数据花费774ms
Thread-26 获取数据花费774ms
Thread-28 获取数据花费774ms
Thread-31 获取数据花费774ms
Thread-12 获取数据花费775ms
Thread-29 获取数据花费774ms
Thread-27 获取数据花费774ms
Thread-18 获取数据花费774ms
Thread-30 获取数据花费774ms
Thread-20 获取数据花费774ms
Thread-24 获取数据花费774ms
Thread-23 获取数据花费781ms
Condition 接口
Condition接口主要的方法有
- await()
- signal()
- signalAll()
类似于Object的wait(),notify(),notifyAll(),Objcet尽量使用notifyAll(),Condition尽量使用signal();
Condition的有边界的,例new ReentrantLock.newCondition().signalAll()
,他的边界为这个Condition,而notifyAll()的边界在于这个对象,只要是这个阻塞在这个对象上的线程,都会通知。
使用condition改善快递通知
public class TestCond {
private static Express express = new Express(100, Express.CITY);
private static class CheckKm implements Runnable {
@Override
public void run() {
express.waitKm();
}
}
private static class CheckSite implements Runnable {
@Override
public void run() {
express.waitSite();
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
new Thread(new CheckKm()).start();
}
for (int j = 0; j < 3; j++) {
new Thread(new CheckSite()).start();
}
Thread.sleep(1000);
express.changeKm();
}
}
Express.class
public class Express {
public static final String CITY = "shanghai";
private int km;
private String site;
private Lock lock = new ReentrantLock();
private Condition kmCond = lock.newCondition();
private Condition siteCond = lock.newCondition();
public Express(int km, String site) {
this.km = km;
this.site = site;
}
public void changeKm() {
lock.lock();
try {
this.km = 101;
kmCond.signal();//1⃣️
//2⃣️ kmCond.signalAll();
} finally {
lock.unlock();
}
}
public void changeSite() {
lock.lock();
try {
this.site = "beijing";
kmCond.signal();//1⃣️
//2⃣️kmCond.signalAll()
} finally {
lock.unlock();
}
}
public void waitSite() {
lock.lock();
try {
while (CITY.equals(this.site)) {
siteCond.await();
System.out.println("checkSite Thread [" + Thread.currentThread().getId() + " ] is be notified");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
System.out.println("the site is " + this.km + ",I will change db");
}
public void waitKm() {
lock.lock();
try {
while (this.km <= 100) {
kmCond.await();
System.out.println("checkKm Thread [" + Thread.currentThread().getId() + " ] is be notified");
}
kmCond.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
System.out.println("the Km is " + this.km + ",I will change db");
}
}
运行1⃣️输出
checkKm Thread [11 ] is be notified
运行2⃣️输出
checkKm Thread [11 ] is be notified
checkKm Thread [12 ] is be notified
checkKm Thread [13 ] is be notified