Solo  当前访客:0 开始使用

Nick1407 的个人博客

公众号:JavaCase

多线程使用(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();

    }
}
0 0