多线程使用(Lock的使用和读写锁)
一、Lock的使用
1、Lock显式锁比synchronized更加灵活,能尝试非阻塞的获取锁、获取锁的过程中可以被中断、超时可以放弃获取锁、可重入等
可重入(默认):在循环或者递归时,会发生锁的重入现象。线程可以再次获取自己内部的锁。比如有线程A获得了某锁,此时锁还没有释放,当线程A再次想要获取这个锁的时候还可获取,如果锁不可重入,则会发生死锁。
2、Lock的基本使用
public class LockCase {
private Lock lock = new ReentrantLock();
private int age = 100000;//初始100000
private static class TestThread extends Thread{
private LockCase lockCase;
public TestThread(LockCase lockCase,String name) {
super(name);
this.lockCase = lockCase;
}
@Override
public void run() {
for(int i=0;i<100000;i++) {//递增100000
lockCase.test();
}
System.out.println(Thread.currentThread().getName()
+" age = "+lockCase.getAge());
}
}
public void test() {
lock.lock();
try {
age++;
} finally {
lock.unlock();
}
}
public void test2() {
lock.lock();
try {
age--;
} finally {
lock.unlock();
}
}
public int getAge() {
return age;
}
public static void main(String[] args) throws InterruptedException {
LockCase lockCase = new LockCase();
Thread endThread = new TestThread(lockCase,"endThread");
endThread.start();
SleepTools.second(1);
for(int i=0;i<100000;i++) {//递减100000
lockCase.test2();
}
System.out.println(Thread.currentThread().getName()
+" age = "+lockCase.getAge());
}
}
2、使用了Lock和Condition实现等待通知
相关操作:获取锁lock()
、释放锁unlock()
、通知signal()
、等待await()
public class ExpressCond {
public final static String CITY = "ShangHai";
private int km;/*快递运输里程数*/
private String site;/*快递到达地点*/
private Lock kmLock = new ReentrantLock();
private Lock siteLock = new ReentrantLock();
private Condition kmCond = kmLock.newCondition();
private Condition siteCond = siteLock.newCondition();
public ExpressCond() {
}
public ExpressCond(int km, String site) {
this.km = km;
this.site = site;
}
/* 变化公里数,然后通知处于wait状态并需要处理公里数的线程进行业务处理*/
public void changeKm(){
kmLock.lock();
try {
this.km = 101;
kmCond.signal();
} finally {
kmLock.unlock();
}
}
/* 变化地点,然后通知处于wait状态并需要处理地点的线程进行业务处理*/
public void changeSite(){
siteLock.lock();
try {
this.site = "BeiJing";
siteCond.signal();
} finally {
siteLock.unlock();
}
}
public void waitKm(){
kmLock.lock();
try {
while(this.km<=100){//公里数小于100不做处理
try {
kmCond.await();
System.out.println("Check Km thread["
+Thread.currentThread().getId()+"] is be signal");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
kmLock.unlock();
}
System.out.println("the Km is "+this.km+",I will change db");
}
public void waitSite(){
siteLock.lock();
try {
while(this.site.equals(CITY)){//快递到达目的地
try {
siteCond.await();
System.out.println("Check Site thread["
+Thread.currentThread().getId()+"] is be signal");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
siteLock.unlock();
}
System.out.println("the site is "+this.site+",I will call user");
}
}
测试:
public class TestCond {
private static ExpressCond express = new ExpressCond(0,ExpressCond.CITY);
/*检查里程数变化的线程,不满足条件,线程一直等待*/
private static class CheckKm extends Thread{
@Override
public void run() {
express.waitKm();
}
}
/*检查地点变化的线程,不满足条件,线程一直等待*/
private static class CheckSite extends Thread{
@Override
public void run() {
express.waitSite();
}
}
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<3;i++){
new CheckSite().start();
}
for(int i=0;i<3;i++){
new CheckKm().start();
}
Thread.sleep(1000);
express.changeKm();//快递地点变化
express.changeSite();
}
}
二、读写锁
读写锁在同一时刻允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。但是可以存在多个读锁,但不能同时存在写锁和读锁。
示例:
实体类
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;
}
}
相关服务的接口
public interface GoodsService {
public GoodsInfo getNum();
public void setNum(int number);
}
用读写锁来实现服务接口
public class UseRwLock implements GoodsService {
private GoodsInfo goodsInfo;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock getLock = lock.readLock();//get
private final Lock setLock = lock.writeLock();//set
public UseRwLock(GoodsInfo goodsInfo) {
this.goodsInfo = goodsInfo;
}
@Override
public GoodsInfo getNum() {
getLock.lock();
SleepTools.ms(5);
try {
return this.goodsInfo;
} finally {
getLock.unlock();
}
}
@Override
public void setNum(int number) {
setLock.lock();
try {
SleepTools.ms(50);
this.goodsInfo.changeNumber(number);
} finally {
setLock.unlock();
}
}
}
测试
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次
GoodsInfo num = goodsService.getNum();
System.out.println("剩余:"+num.getStoreNumber());
}
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();
Random r = new Random();
for(int i=0;i<10;i++){//操作10次
SleepTools.ms(50);
goodsService.setNum(r.nextInt(10));
}
System.out.println(Thread.currentThread().getName()
+"写商品数据耗时:"+(System.currentTimeMillis()-start)+"ms---------");
}
}
public static void main(String[] args) throws InterruptedException {
GoodsInfo goodsInfo = new GoodsInfo("Cup",100000,10000);
GoodsService goodsService = new UseRwLock(goodsInfo);
for(int i = 0;i<minthreadCount;i++){
Thread setT = new Thread(new SetThread(goodsService));
for(int j=0;j<readWriteRatio;j++) {
Thread getT = new Thread(new GetThread(goodsService));
getT.start();
}
setT.start();
}
latch.countDown();
}
}