• Java多线程编程核心技术--Lock的使用(一)


    使用ReentrantLock类

    在Java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,但在JDK1.5中新增加了ReentrantLock类也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加灵活。

    使用ReentrantLock实现同步:测试1
    public class Service {
    	private Lock lock = new ReentrantLock();
    	public void testMethod() {
    		lock.lock();
    		for (int i = 0; i < 5; i++) {
    			System.out.println(Thread.currentThread().getName() + " i=" + (i + 1));
    		}
    		lock.unlock();
    	}
    }
    
    public class MyThread extends Thread {
    	private Service service;
    	public MyThread(Service service) {
    		super();
    		this.service = service;
    	}
    	
    	@Override
    	public void run() {
    		service.testMethod();
    	}
    }
    
    public class Main {
    	public static void main(String[] args) {
    		Service service = new Service();
    		MyThread t1 = new MyThread(service);
    		MyThread t2 = new MyThread(service);
    		MyThread t3 = new MyThread(service);
    		MyThread t4 = new MyThread(service);
    		MyThread t5 = new MyThread(service);
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    		t5.start();
    	}
    }
    

    控制台打印结果如下:

    Thread-3 i=1
    Thread-3 i=2
    Thread-3 i=3
    Thread-3 i=4
    Thread-3 i=5
    Thread-0 i=1
    Thread-0 i=2
    Thread-0 i=3
    Thread-0 i=4
    Thread-0 i=5
    Thread-1 i=1
    Thread-1 i=2
    Thread-1 i=3
    Thread-1 i=4
    Thread-1 i=5
    Thread-2 i=1
    Thread-2 i=2
    Thread-2 i=3
    Thread-2 i=4
    Thread-2 i=5
    Thread-4 i=1
    Thread-4 i=2
    Thread-4 i=3
    Thread-4 i=4
    Thread-4 i=5
    

    可见,当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印。线程打印数据是分组打印,因为当前线程已经持有锁,但线程之间打印的顺序是随机的。


    使用ReentrantLock实现同步:测试2
    public class Service {
    	private Lock lock = new ReentrantLock();
    	public void methodA() {
    		try {
    			lock.lock();
    			System.out.println("methodA begin " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
    			Thread.sleep(5000);
    			System.out.println("methodA end " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally {
    			lock.unlock();
    		}
    	}
    	public void methodB() {
    		try {
    			lock.lock();
    			System.out.println("methodB begin " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
    			Thread.sleep(5000);
    			System.out.println("methodB end " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally {
    			lock.unlock();
    		}
    	}
    }
    
    public class ThreadA extends Thread {
    	private Service service;
    	public ThreadA(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		service.methodA();
    	}
    }
    
    public class ThreadAA extends Thread {
    	private Service service;
    	public ThreadAA(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		service.methodA();
    	}
    }
    
    public class ThreadB extends Thread {
    	private Service service;
    	public ThreadB(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		service.methodA();
    	}
    }
    
    public class ThreadBB extends Thread {
    	private Service service;
    	public ThreadBB(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		service.methodA();
    	}
    }
    
    public class Main {
    	public static void main(String[] args) {
    		Service service = new Service();
    		ThreadA a = new ThreadA(service);
    		a.setName("A");
    		a.start();
    		ThreadAA aa = new ThreadAA(service);
    		aa.setName("AA");
    		aa.start();
    		ThreadB b = new ThreadB(service);
    		b.setName("B");
    		b.start();
    		ThreadBB bb = new ThreadBB(service);
    		bb.setName("BB");
    		bb.start();
    	}
    }
    

    控制台打印结果如下:

    methodA begin A time=1467162810185
    methodA end A time=1467162815186
    methodA begin AA time=1467162815186
    methodA end AA time=1467162820187
    methodA begin B time=1467162820187
    methodA end B time=1467162825187
    methodA begin BB time=1467162825187
    methodA end BB time=1467162830188
    

    可见,调用lock.lock()代码的线程就持有了“对象监视器”,其他线程只能等待锁被释放时再次争抢。效果和使用synchronized关键字一样,线程之间还是顺序执行的。


    使用condition实现等待/通知:错误用法与解决

    关键字synchronized与wait()和notify()/notifyAll()方法结合可以实现等待/通知模式,类ReentrantLock也可以实现同样的功能,但需要借助Condition对象。Condition类是在JDK5中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在知道的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。

    在使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是非常重要的,而且在Condition类中是默认提供的。

    而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。

    看以下代码:

    public class Service {
    	private Lock lock = new ReentrantLock();
    	private Condition condition = lock.newCondition();
    	public void await() {
    		try {
    			condition.await();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    public class ThreadA extends Thread {
    	private Service service;
    	public ThreadA(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		service.await();
    	}
    }
    
    public class Main {
    	public static void main(String[] args) {
    		Service service = new Service();
    		ThreadA a = new ThreadA(service);
    		a.start();
    	}
    }
    

    运行以上代码,控制台抛出异常:

    java.lang.IllegalMonitorStateException
    	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(Unknown Source)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(Unknown Source)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(Unknown Source)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
    	at com.umgsai.thread.thread46.Service.await(Service.java:14)
    	at com.umgsai.thread.thread46.ThreadA.run(ThreadA.java:11)
    
    

    异常信息是监视器出错,解决的办法是必须在condition.await()方法调用之前调用lock.lock()代码获得同步监视器。将Service类做如下修改:

    public class Service {
    	private Lock lock = new ReentrantLock();
    	private Condition condition = lock.newCondition();
    	public void await() {
    		try {
    			lock.lock();
    			System.out.println("A");
    			condition.await();
    			System.out.println("B");
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    			System.out.println("锁被释放了");
    		}
    	}
    }
    

    重新运行程序,控制台打印结果如下:

    A
    

    程序不结束,原因是调用了Condition对象的await()方法,使当前执行任务的线程进入了等待WAITING状态。


    正确使用Condition实现等待/通知
    public class Service {
    	private Lock lock = new ReentrantLock();
    	public Condition condition = lock.newCondition();
    	public void await() {
    		try {
    			lock.lock();
    			System.out.println(Thread.currentThread().getName() + " await时间为" + System.currentTimeMillis());
    			condition.await();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    	public void signal() {
    		try {
    			lock.lock();
    			System.out.println(Thread.currentThread().getName() + " signal时间为" + System.currentTimeMillis());
    			condition.signal();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    }
    
    public class ThreadA extends Thread {
    	private Service service;
    	public ThreadA(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		service.await();
    	};
    }
    
    public class Main {
    	public static void main(String[] args) throws InterruptedException {
    		Service service = new Service();
    		ThreadA a = new ThreadA(service);
    		a.start();
    		Thread.sleep(2000);
    		service.signal();
    	}
    }
    

    控制台打印结果如下:

    Thread-0 await时间为1467293210228
    main signal时间为1467293212227
    

    成功实现等待/通知模式。

    Object类中的wait()等待相当于Condition类中的await()方法。

    Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。

    Object类中的notify()方法相当于Condition类中的signal()方法。

    Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。


    使用多个Condition实现通知部分线程:错误用法
    public class Service {
    	private Lock lock = new ReentrantLock();
    	public Condition condition = lock.newCondition();
    	public void awaitA() {
    		try {
    			lock.lock();
    			Thread.sleep(500);
    			System.out.println(Thread.currentThread().getName() + " begin awaitA时间为" + System.currentTimeMillis());
    			condition.await();//释放锁
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				Thread.sleep(500);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + " end awaitA时间为" + System.currentTimeMillis());
    			lock.unlock();
    		}
    	}
    	
    	public void awaitB() {
    		try {
    			lock.lock();
    			Thread.sleep(500);
    			System.out.println(Thread.currentThread().getName() + " begin awaitB时间为" + System.currentTimeMillis());
    			condition.await();//释放锁
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				Thread.sleep(500);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + " end awaitB时间为" + System.currentTimeMillis());
    			lock.unlock();
    		}
    	}
    	
    	public void signalAll() {
    		try {
    			lock.lock();
    			System.out.println(Thread.currentThread().getName() + " end signalAll时间为" + System.currentTimeMillis());
    			condition.signalAll();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    }
    
    public class ThreadA extends Thread {
    	private Service service;
    	public ThreadA(Service service) {
    		super();
    		this.service = service;
    	}
    	
    	@Override
    	public void run() {
    		service.awaitA();
    	}
    }
    
    public class ThreadB extends Thread {
    	private Service service;
    	public ThreadB(Service service) {
    		super();
    		this.service = service;
    	}
    	
    	@Override
    	public void run() {
    		service.awaitB();
    	}
    }
    
    public class Main {
    	public static void main(String[] args) throws InterruptedException {
    		Service service = new Service();
    		ThreadA a = new ThreadA(service);
    		a.setName("A");
    		a.start();
    		ThreadB b = new ThreadB(service);
    		b.setName("B");
    		b.start();
    		Thread.sleep(3000);
    		service.signalAll();
    	}
    }
    

    控制台打印结果如下:

    A begin awaitA时间为1467293693906
    B begin awaitB时间为1467293694407
    main end signalAll时间为1467293696406
    A end awaitA时间为1467293696906
    B end awaitB时间为1467293697407
    

    程序运行后A和B都被唤醒了。


    使用多个Condition实现通知部分线程:正确用法

    将上面例子中Service作如下修改:

    public class Service {
    	private Lock lock = new ReentrantLock();
    	public Condition conditionA = lock.newCondition();
    	public Condition conditionB = lock.newCondition();
    	public void awaitA() {
    		try {
    			lock.lock();
    			Thread.sleep(500);
    			System.out.println(Thread.currentThread().getName() + " begin awaitA时间为" + System.currentTimeMillis());
    			conditionA.await();//释放锁
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				Thread.sleep(500);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + " end awaitA时间为" + System.currentTimeMillis());
    			lock.unlock();
    		}
    	}
    	
    	public void awaitB() {
    		try {
    			lock.lock();
    			Thread.sleep(500);
    			System.out.println(Thread.currentThread().getName() + " begin awaitB时间为" + System.currentTimeMillis());
    			conditionB.await();//释放锁
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				Thread.sleep(500);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + " end awaitB时间为" + System.currentTimeMillis());
    			lock.unlock();
    		}
    	}
    	
    	public void signalAll_A() {
    		try {
    			lock.lock();
    			System.out.println(Thread.currentThread().getName() + " end signalAll_A时间为" + System.currentTimeMillis());
    			conditionA.signalAll();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    	
    	public void signalAll_B() {
    		try {
    			lock.lock();
    			System.out.println(Thread.currentThread().getName() + " end signalAll_B时间为" + System.currentTimeMillis());
    			conditionB.signalAll();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    }
    
    public class Main {
    	public static void main(String[] args) throws InterruptedException {
    		Service service = new Service();
    		ThreadA a = new ThreadA(service);
    		a.setName("A");
    		a.start();
    		ThreadB b = new ThreadB(service);
    		b.setName("B");
    		b.start();
    		Thread.sleep(3000);
    		service.signalAll_A();//通知在conditionA上等待的线程
    		Thread.sleep(2000);
    		service.signalAll_B();//通知在conditionB上等待的线程
    	}
    }
    

    控制台打印结果如下:

    A begin awaitA时间为1467293966937
    B begin awaitB时间为1467293967437
    main end signalAll_A时间为1467293969438
    A end awaitA时间为1467293969938
    main end signalAll_B时间为1467293971438
    B end awaitB时间为1467293971939
    

    实现生产者/消费者模式:一对一交替打印
    public class Service {
    	private ReentrantLock lock = new ReentrantLock();
    	private Condition condition = lock.newCondition();
    	private boolean hasValue = false;
    	public void set() {
    		try {
    			lock.lock();
    			while (hasValue) {
    				condition.await();
    			}
    			System.out.println("★");
    			hasValue = true;
    			condition.signal();//通知在condition上等待的线程
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    	public void get() {
    		try {
    			lock.lock();
    			while (!hasValue) {
    				condition.await();
    			}
    			System.out.println("☆");
    			hasValue = false;
    			condition.signal();//通知在condition上等待的线程
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    }
    
    public class ThreadA extends Thread {
    	private Service service;
    	public ThreadA(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		for (int i = 0; i < Integer.MAX_VALUE; i++) {
    			service.set();	
    		}
    	}
    }
    
    public class ThreadB extends Thread {
    	private Service service;
    	public ThreadB(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		for (int i = 0; i < Integer.MAX_VALUE; i++) {
    			service.get();	
    		}
    	}
    }
    
    public class Main {
    	public static void main(String[] args) {
    		Service service = new Service();
    		ThreadA a = new ThreadA(service);
    		ThreadB b = new ThreadB(service);
    		a.start();
    		b.start();
    	}
    }
    

    控制台打印结果如下:

    ......
    ★
    ☆
    ★
    ☆
    ★
    ☆
    ......
    

    将以上代码做如下修改:

    public class Main {
    	public static void main(String[] args) {
    		Service service = new Service();
    		ThreadA[] a = new ThreadA[10];
    		ThreadB[] b = new ThreadB[10];
    		for (int i = 0; i < 10; i++) {
    			a[i] = new ThreadA(service);
    			a[i].start();
    			b[i] = new ThreadB(service);
    			b[i].start();
    		}
    	}
    }
    

    此时控制台打印结果如下:

    ......
    有可能★连续
    ☆
    ★
    有可能★连续
    ☆
    有可能☆连续
    ★
    有可能★连续
    ☆
    有可能☆连续
    ★
    有可能★连续
    ☆
    有可能☆连续
    有可能☆连续
    有可能☆连续
    ★
    有可能★连续
    有可能★连续
    ....
    

    程序运行一段时间后出现假死现象。前面有程序和这个程序的原理一样。解决方法是修改Service里面的condition.signal()为condition.signalAll()。


    公平锁与非公平锁
    /**
    * 公平锁
    */
    public class Service {
    	private ReentrantLock lock;
    	public Service(boolean isFair) {
    		super();
    		lock = new ReentrantLock(isFair);
    	}
    	
    	public void serviceMethod() {
    		try {
    			lock.lock();
    			System.out.println(Thread.currentThread().getName() + "获得锁定");
    		} finally{
    			lock.unlock();
    		}
    	}
    }
    
    public class RunFair {
    	public static void main(String[] args) {
    		final Service service = new Service(true);
    		Runnable runnable = new Runnable() {
    			
    			@Override
    			public void run() {
    				System.out.println(Thread.currentThread().getName() + "运行了");
    				service.serviceMethod();
    			}
    		};
    		Thread[] threads = new Thread[10];
    		for (int i = 0; i < threads.length; i++) {
    			threads[i] = new Thread(runnable);
    		}
    		for (int i = 0; i < threads.length; i++) {
    			threads[i].start();;
    		}
    	}
    }
    

    控制台打印结果如下:

    Thread-0运行了
    Thread-1运行了
    Thread-0获得锁定
    Thread-1获得锁定
    Thread-2运行了
    Thread-2获得锁定
    Thread-3运行了
    Thread-4运行了
    Thread-3获得锁定
    Thread-4获得锁定
    Thread-5运行了
    Thread-5获得锁定
    Thread-6运行了
    Thread-8运行了
    Thread-6获得锁定
    Thread-7运行了
    Thread-8获得锁定
    Thread-7获得锁定
    Thread-9运行了
    Thread-9获得锁定
    

    打印结果基本呈有序的状态,这就是公平锁的特点。


    getHoldCount方法
    /**
     * getHoldCount()方法查询当前线程保持此锁定的个数
     * 也就是调用()方法的次数
     */
    public class Service {
    	private ReentrantLock lock = new ReentrantLock();
    	public void serviceMethod1() {
    		try {
    			lock.lock();
    			System.out.println("serviceMethod1 getHoldCount=" + lock.getHoldCount());//1
    			serviceMethod2();
    		} finally{
    			lock.unlock();
    		}
    	}
    	public void serviceMethod2() {
    		try {
    			lock.lock();
    			System.out.println("serviceMethod2 getHoldCount=" + lock.getHoldCount());//2
    		} finally{
    			lock.unlock();
    		}
    	}
    	public static void main(String[] args) {
    		Service service = new Service();
    		service.serviceMethod1();
    	}
    }
    
    
    getQueueLength()方法
    //getQueueLength返回正在等待此锁定的线程估计数
    public class MyService {
    	public ReentrantLock lock = new ReentrantLock();
    	public void serviceMethod1() {
    		try {
    			lock.lock();
    			System.out.println(Thread.currentThread().getName() + "进入方法");
    			Thread.sleep(Integer.MAX_VALUE);
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    	public static void main(String[] args) throws InterruptedException {
    		final MyService myService = new MyService();
    		Runnable runnable = new Runnable() {
    			
    			@Override
    			public void run() {
    				myService.serviceMethod1();
    			}
    		};
    		Thread[] threads = new Thread[10];
    		for (int i = 0; i < threads.length; i++) {
    			threads[i] = new Thread(runnable);
    		}
    		for (int i = 0; i < threads.length; i++) {
    			threads[i].start();
    		}
    		Thread.sleep(2000);
    		System.out.println(myService.lock.getQueueLength() + "在等待获取锁");
    	}
    }
    

    控制台打印结果如下:

    Thread-0进入方法
    9在等待获取锁
    
    getWaitQueueLength()方法
    public class MyService1 {
    	private ReentrantLock lock = new ReentrantLock();
    	private Condition condition = lock.newCondition();
    	public void waitMethod() {
    		try {
    			lock.lock();
    			condition.await();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    	
    	public void notifyMethod() {
    		try {
    			lock.lock();
    			System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//10
    			condition.signal();
    			System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//9
    			condition.signal();
    			System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//8
    			condition.signalAll();
    			System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//7
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    	
    	public static void main(String[] args) throws InterruptedException {
    		final MyService1 service = new MyService1();
    		Runnable runnable = new Runnable() {
    			
    			@Override
    			public void run() {
    				service.waitMethod();
    			}
    		};
    		Thread[] threads = new Thread[10];
    		for (int i = 0; i < threads.length; i++) {
    			threads[i] = new Thread(runnable);
    		}
    		for (int i = 0; i < threads.length; i++) {
    			threads[i].start();
    		}
    		Thread.sleep(2000);
    		service.notifyMethod();
    	}
    }
    

    控制台打印结果如下:

    10个线程在等待condition
    9个线程在等待condition
    8个线程在等待condition
    0个线程在等待condition
    

    getWaitQueueLength(condition)方法返回等待与此锁定相关的给定条件condition的线程估计数。

    public class Service {
    	public ReentrantLock lock = new ReentrantLock();
    	public Condition condition = lock.newCondition();
    	public void waitMethod() {
    		try {
    			lock.lock();//获得锁
    			Thread.sleep(Integer.MAX_VALUE);
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    	public static void main(String[] args) throws InterruptedException {
    		final Service service = new Service();
    		Runnable runnable = new Runnable() {
    			
    			@Override
    			public void run() {
    				service.waitMethod();
    			}
    		};
    		Thread a = new Thread(runnable);
    		a.start();
    		Thread.sleep(500);
    		Thread b = new Thread(runnable);
    		b.start();
    		Thread.sleep(500);
    		System.out.println(service.lock.hasQueuedThread(a));//false 查询指定的线程是否正在等待此锁定
    		System.out.println(service.lock.hasQueuedThread(b));//true
    		System.out.println(service.lock.hasQueuedThreads());//true 查询是否有线程正在等待此锁
    	}
    }
    

    public class MyService {
    	private ReentrantLock lock = new ReentrantLock();
    	private Condition condition = lock.newCondition();
    	public void waitMethod() {
    		try {
    			lock.lock();
    			condition.await();//进入WAITTING状态,释放锁
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    	public void notifyMethod() {
    		try {
    			lock.lock();
    			System.out.println("是否有线程正在等待condition:" + lock.hasWaiters(condition));
    			System.out.println("正在等待condition的线程数是:" + lock.getWaitQueueLength(condition));//10个线程等待
    			condition.signal();//唤醒一个线程
    			System.out.println("正在等待condition的线程数是:" + lock.getWaitQueueLength(condition));//9个线程等待
    			condition.signalAll();//剩下的个全部唤醒
    			System.out.println("正在等待condition的线程数是:" + lock.getWaitQueueLength(condition));//0个线程等待
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally {
    			lock.unlock();
    		}
    	}
    	public static void main(String[] args) throws InterruptedException {
    		final MyService service = new MyService();
    		Runnable runnable = new Runnable() {
    			
    			@Override
    			public void run() {
    				service.waitMethod();
    			}
    		};
    		Thread[] threads = new Thread[10];
    		for (int i = 0; i < threads.length; i++) {
    			threads[i] = new Thread(runnable);
    			threads[i].start();
    		}
    		Thread.sleep(2000);
    		service.notifyMethod();
    	}
    }
    

    使用Condition实现顺序执行
    public class Run {
    	volatile private static int nextPrintWho = 1;
    	private static ReentrantLock lock = new ReentrantLock();
    	final private static Condition conditionA = lock.newCondition();
    	final private static Condition conditionB = lock.newCondition();
    	final private static Condition conditionC = lock.newCondition();
    	public static void main(String[] args) {
    		Thread threadA = new Thread(){
    			public void run() {
    				try {
    					lock.lock();
    					while (nextPrintWho != 1) {
    						conditionA.await();
    					}
    					for (int i = 0; i < 3; i++) {
    						System.out.println("ThreadA " + (i + 1));
    					}
    					nextPrintWho = 2;
    					conditionB.signalAll();//唤醒B组
    				} catch (Exception e) {
    					e.printStackTrace();
    				} finally {
    					lock.unlock();
    				}
    			}
    		};
    		Thread threadB = new Thread(){
    			public void run() {
    				try {
    					lock.lock();
    					while (nextPrintWho != 2) {
    						conditionB.await();
    					}
    					for (int i = 0; i < 3; i++) {
    						System.out.println("ThreadB " + (i + 1));
    					}
    					nextPrintWho = 3;
    					conditionC.signalAll();//唤醒C组
    				} catch (Exception e) {
    					e.printStackTrace();
    				} finally {
    					lock.unlock();
    				}
    			}
    		};
    		Thread threadC = new Thread(){
    			public void run() {
    				try {
    					lock.lock();
    					while (nextPrintWho != 3) {
    						conditionC.await();
    					}
    					for (int i = 0; i < 3; i++) {
    						System.out.println("ThreadC " + (i + 1) + " " + System.currentTimeMillis());
    					}
    					nextPrintWho = 1;
    					conditionA.signalAll();//唤醒A组
    				} catch (Exception e) {
    					e.printStackTrace();
    				} finally {
    					lock.unlock();
    				}
    			}
    		};
    		Thread[] aThreads = new Thread[5];
    		Thread[] bThreads = new Thread[5];
    		Thread[] cThreads = new Thread[5];
    		for (int i = 0; i < cThreads.length; i++) {
    			aThreads[i] = new Thread(threadA);
    			bThreads[i] = new Thread(threadB);
    			cThreads[i] = new Thread(threadC);
    			aThreads[i].start();
    			bThreads[i].start();
    			cThreads[i].start();
    		}
    	}
    }
    

    控制台输入结果如下:

    ThreadA 1
    ThreadA 2
    ThreadA 3
    ThreadB 1
    ThreadB 2
    ThreadB 3
    ThreadC 1 1468067302315
    ThreadC 2 1468067302315
    ThreadC 3 1468067302315
    ThreadA 1
    ThreadA 2
    ThreadA 3
    ThreadB 1
    ThreadB 2
    ThreadB 3
    ThreadC 1 1468067302315
    ThreadC 2 1468067302315
    ThreadC 3 1468067302315
    ThreadA 1
    ThreadA 2
    ThreadA 3
    ThreadB 1
    ThreadB 2
    ThreadB 3
    ThreadC 1 1468067302315
    ThreadC 2 1468067302315
    ThreadC 3 1468067302315
    ThreadA 1
    ThreadA 2
    ThreadA 3
    ThreadB 1
    ThreadB 2
    ThreadB 3
    ThreadC 1 1468067302316
    ThreadC 2 1468067302316
    ThreadC 3 1468067302316
    ThreadA 1
    ThreadA 2
    ThreadA 3
    ThreadB 1
    ThreadB 2
    ThreadB 3
    ThreadC 1 1468067302316
    ThreadC 2 1468067302316
    ThreadC 3 1468067302316
    

    使用ReentrantReadWriteLock类

    ReentrantLock具有完全互斥排他的效果,同一时刻只有一个线程在执行ReentrantLock.lock()方法后面的任务。使用读写锁ReentrantReadWriteLock可以区分读写操作。读操作不互斥时,可以使用读锁让多个线程同时读。写锁互斥时,同一时刻值允许一个线程写。

    读读共享
    public class Service {
    	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    	public void read() {
    		try {
    			try {
    				lock.readLock().lock();
    				System.out.println(Thread.currentThread().getName() + "获得读锁" + System.currentTimeMillis());
    				Thread.sleep(1000);
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				lock.readLock().unlock();
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    public class ThreadA extends Thread {
    	private Service service;
    	public ThreadA(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		service.read();
    	}
    }
    
    public class ThreadB extends Thread {
    	private Service service;
    	public ThreadB(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		service.read();
    	}
    }
    
    public class Run {
    	public static void main(String[] args) {
    		Service service = new Service();
    		ThreadA a = new ThreadA(service);
    		ThreadB b = new ThreadB(service);
    		a.start();
    		b.start();
    	}
    }
    

    程序运行结果如下:

    Thread-0获得读锁1468068667767
    Thread-1获得读锁1468068667767
    

    可以看出,两个线程同时进入lock()方法后面的代码。


    写写互斥

    将以上代码的Service类做如下修改:

    public class Service {
    	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    	public void write() {
    		try {
    			try {
    				lock.writeLock().lock();
    				System.out.println(Thread.currentThread().getName() + "获得写锁" + System.currentTimeMillis());
    				Thread.sleep(1000);
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				lock.writeLock().unlock();
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    程序运行结果如下:

    Thread-0获得写锁1468069134317
    Thread-1获得写锁1468069135322
    

    可见此时两个线程以排队方式执行lock()后面的代码。


    读写互斥
    public class Service {
    	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    	public void read() {
    		try {
    			try {
    				lock.readLock().lock();
    				System.out.println(Thread.currentThread().getName() + "获得读锁" + System.currentTimeMillis());
    				Thread.sleep(1000);
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				lock.readLock().unlock();
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	public void write() {
    		try {
    			try {
    				lock.writeLock().lock();
    				System.out.println(Thread.currentThread().getName() + "获得写锁" + System.currentTimeMillis());
    				Thread.sleep(1000);
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				lock.writeLock().unlock();
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    public class ThreadA extends Thread {
    	private Service service;
    	public ThreadA(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		service.read();
    	}
    }
    
    public class ThreadB extends Thread {
    	private Service service;
    	public ThreadB(Service service) {
    		super();
    		this.service = service;
    	}
    	@Override
    	public void run() {
    		service.write();
    	}
    }
    
    public class Run {
    	public static void main(String[] args) {
    		Service service = new Service();
    		ThreadA a = new ThreadA(service);
    		ThreadB b = new ThreadB(service);
    		a.start();
    		b.start();
    	}
    }
    

    程序运行结果如下:

    Thread-0获得读锁1468069343031
    Thread-1获得写锁1468069344033
    

    两个线程以排队方式执行lock()后面的代码,可见读写操作是互斥的。

    将以上代码做如下修改:

    public class Run {
    	public static void main(String[] args) {
    		Service service = new Service();
    		ThreadA a = new ThreadA(service);
    		ThreadB b = new ThreadB(service);
    		b.start();//先启动写线程
    		a.start();//后启动读线程
    	}
    }
    

    此时程序运行结果如下:

    Thread-1获得写锁1468069625161
    Thread-0获得读锁1468069626166
    

    可见,两个线程也是以排队方式执行lock()后面的代码的。

    综合ReentrantReadWriteLock类的几个实体程序,“读写” “写写” “写读”都是互斥的。“读读”是异步的,非互斥的。

  • 相关阅读:
    history对象
    排序算法总结
    Boltzmann机
    Sort Colors
    First Missing Positive
    Sort List
    Insertion Sort List
    Merge Two Sorted Lists
    Merge Sorted Array
    Sum Root to Leaf Numbers
  • 原文地址:https://www.cnblogs.com/umgsai/p/5600103.html
Copyright © 2020-2023  润新知