Java多线程(四)

使用ReentrantLock类

java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,而ReentrantLock也能达到同样的效果,并且比synchronized更加的灵活


图书馆有一台电脑,学生们可以通过电脑查询想要的图书在哪个书架上

代码4-1

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class Computer {
	private Lock lock = new ReentrantLock();


	public void select() {
		lock.lock();
		System.out.println(Thread.currentThread().getName() + "查阅图书");
		System.out.println(Thread.currentThread().getName() + "查阅完毕");
		lock.unlock();
	}


}


class Student extends Thread {


	private Computer computer;


	public Student(String name, Computer computer) {
		super(name);
		this.computer = computer;
	}


	public void run() {
		computer.select();
	}


}


public class Library {


	public static void main(String[] args) {
		Computer computer = new Computer();
		for (int i = 0; i < 6; i++) {
			new Student("学生" + (i + 1) + "号", computer).start();
		}
	}
}



代码4-1运行结果:

学生2号查阅图书
学生2号查阅完毕
学生6号查阅图书
学生6号查阅完毕
学生1号查阅图书
学生1号查阅完毕
学生3号查阅图书
学生3号查阅完毕
学生4号查阅图书
学生4号查阅完毕
学生5号查阅图书
学生5号查阅完毕


从运行的结果来看,当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印,但线程之间打印的顺序是随机的


使用Condition实现等待/通知

关键字synchronized与wait()、notify()、notifyAll()方法相结合实现等待/通知模式,ReentrantLock类也可以实现同样的功能,但需要借助Condition对象,使用Condition可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(即监视器对象)实例,线程对象可以注册再指定的Condition中,从而可以有选择性的进行线程通知,相比使用notify()和notifyAll()方法进行通知,被通知的线程却是由JVM随机选择的,提高了灵活度。

synchronized相当于整个Lock对象只有单一的Condition对象,所有线程都注册再它一个对象身上。线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权


模拟一个老师给3个学生发水果的游戏,count为老师所拥有的水果数量,初始数量为2,每当count为0时,若处在分发水果方法的学生线程,会陷入等待状态,如果已经取完水果的学生,还要再取水果,会先查看老师的水果数量是否为0,如果为0则调用添加水果的方法通知老师添加水果,当老师准备好水果,又会通知所有等待的学生线程继续来获取水果

代码4-2

import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class Teacher {
	private Random random = new Random();
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	private int count = 2;// 老师所拥有的水果数量


	public void takeFruits() {
		System.out.println(Thread.currentThread().getName() + "申请水果");
		try {
			Thread.sleep(random.nextInt(800));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		try {
			// 调用condition.await()方法前必须先获取锁定,否则会报错
			lock.lock();
			if (count == 0) {
				System.out.println(Thread.currentThread().getName() + "等待水果");
				condition.await();
			}
			if (count != 0) {
				count--;
				System.out.println(Thread.currentThread().getName() + "获得水果");
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			lock.unlock();
		}


	}


	public void addFruits() {
		System.out.println("老师检查水果数量");
		try {
			Thread.sleep(random.nextInt(800));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		lock.lock();
		if (count == 0) {
			count += 2;
			System.out.println("老师准备水果");
			condition.signalAll();
		}
		lock.unlock();
	}


	public int getCount() {
		return count;
	}


}


class Student extends Thread {
	private Teacher teacher;


	public Student(String name, Teacher teacher) {
		super(name);
		this.teacher = teacher;
	}


	@Override
	public void run() {
		while (true) {
			if (teacher.getCount() == 0) {
				teacher.addFruits();
			} else {
				teacher.takeFruits();
			}
		}
	}
}


public class EatFruit {


	public static void main(String[] args) {
		Teacher teacher = new Teacher();
		for (int i = 0; i < 3; i++) {
			new Student("学生" + (i + 1) + "号", teacher).start();
		}


	}


}



代码4-2运行结果:

学生1号申请水果
学生3号申请水果
学生2号申请水果
学生3号获得水果
学生3号申请水果
学生1号获得水果
老师检查水果数量
学生2号等待水果
学生3号等待水果
老师准备水果
学生1号申请水果
学生2号获得水果
学生2号申请水果
学生3号获得水果
老师检查水果数量
学生2号等待水果
学生1号等待水果

…………



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

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

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

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


使用多个Condition实现通知部分线程

修改老师和学生分水果的游戏,现在,学生分男孩和女孩,男生可以从老师那边拿走苹果,女生可以从老师那边拿走香蕉,当老师苹果或香蕉的数量为0的时候,男生或女生必须陷入等待,当老师添加苹果数量时,需要唤醒等待的男孩线程,同理,当老师添加香蕉数量时,也需要唤醒正在等待香蕉的女孩线程

代码4-3

import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Teacher {
	private Random random = new Random();
	private Lock lock = new ReentrantLock();
	private Condition appleCondition = lock.newCondition();
	private Condition bananaCondition = lock.newCondition();
	private int appleCount = 2;// 老师所拥有的苹果数量
	private int bananaCount = 1;// 老师所拥有的香蕉数量

	public void takeApple() {
		System.out.println(Thread.currentThread().getName() + "申请苹果");
		try {
			Thread.sleep(random.nextInt(800));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		try {
			// 调用condition.await()方法前必须先获取锁定,否则会报错
			lock.lock();
			if (appleCount == 0) {
				System.out.println(Thread.currentThread().getName() + "等待苹果");
				appleCondition.await();
			}
			if (appleCount != 0) {
				appleCount--;
				System.out.println(Thread.currentThread().getName() + "获得苹果");
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void takeBanana() {
		System.out.println(Thread.currentThread().getName() + "申请香蕉");
		try {
			Thread.sleep(random.nextInt(800));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		try {
			// 调用condition.await()方法前必须先获取锁定,否则会报错
			lock.lock();
			if (bananaCount == 0) {
				System.out.println(Thread.currentThread().getName() + "等待香蕉");
				bananaCondition.await();
			}
			if (bananaCount != 0) {
				bananaCount--;
				System.out.println(Thread.currentThread().getName() + "获得香蕉");
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void addApple() {
		System.out.println("老师检查苹果数量");
		try {
			Thread.sleep(random.nextInt(800));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		lock.lock();
		if (appleCount == 0) {
			appleCount += 2;
			System.out.println("老师准备苹果");
			appleCondition.signalAll();
		}
		lock.unlock();
	}

	public void addBanana() {
		System.out.println("老师检查香蕉数量");
		try {
			Thread.sleep(random.nextInt(800));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		lock.lock();
		if (bananaCount == 0) {
			bananaCount += 1;
			System.out.println("老师准备香蕉");
			bananaCondition.signalAll();
		}
		lock.unlock();
	}

	public int getAppleCount() {
		return appleCount;
	}

	public int getBananaCount() {
		return bananaCount;
	}
}

class Boy extends Thread {
	private Teacher teacher;

	public Boy(String name, Teacher teacher) {
		super(name);
		this.teacher = teacher;
	}

	@Override
	public void run() {
		while (true) {
			if (teacher.getAppleCount() == 0) {
				teacher.addApple();
			}
			teacher.takeApple();
		}
	}
}

class Girl extends Thread {
	private Teacher teacher;

	public Girl(String name, Teacher teacher) {
		super(name);
		this.teacher = teacher;
	}

	@Override
	public void run() {
		while (true) {
			if (teacher.getBananaCount() == 0) {
				teacher.addBanana();
			}
			teacher.takeBanana();
		}
	}
}

public class EatFruits {

	public static void main(String[] args) {
		Teacher teacher = new Teacher();
		for (int i = 0; i < 3; i++) {
			new Boy("男孩" + (i + 1) + "号", teacher).start();
			new Girl("女孩" + (i + 1) + "号", teacher).start();
		}
	}

}

代码4-3运行结果:

男孩1号申请苹果
女孩1号申请香蕉
男孩2号申请苹果
女孩2号申请香蕉
男孩3号申请苹果
女孩3号申请香蕉
女孩1号获得香蕉
老师检查香蕉数量
老师准备香蕉
女孩1号申请香蕉
男孩2号获得苹果
男孩2号申请苹果
男孩3号获得苹果
老师检查苹果数量
男孩1号等待苹果
女孩2号获得香蕉
老师检查香蕉数量
女孩3号等待香蕉
老师准备苹果
男孩3号申请苹果
男孩1号获得苹果

…………


锁分为公平锁和不公平锁,公平锁,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO先进先出的原则等待获取锁。非公平锁则是抢占机制,上来就直接尝试占有锁,如果尝试失败,就再采用类似公平锁那种方式

代码4-4

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class Monkey extends Thread {


	private Food food;


	public Monkey(String name, Food food) {
		super(name);
		this.food = food;
	}


	@Override
	public void run() {
		System.out.println(getName() + "等待香蕉");
		food.eatBanana();
	}


}


class People extends Thread {
	private Food food;


	public People(String name, Food food) {
		super(name);
		this.food = food;
	}


	@Override
	public void run() {
		System.out.println(getName() + "等待面包");
		food.eatBread();
	}
}


public class Food {
	private static final int NUM = 5;
	public static final Lock FAIR = new ReentrantLock(true);
	public static final Lock UNFAIR = new ReentrantLock(false);


	public void eatBread() {
		try {
			FAIR.lock();
			System.out.println(Thread.currentThread().getName() + "得到面包  ");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			FAIR.unlock();
		}
	}


	public void eatBanana() {
		try {
			UNFAIR.lock();
			System.out.println(Thread.currentThread().getName() + "得到香蕉  ");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			UNFAIR.unlock();
		}
	}


	public static void main(String[] args) throws InterruptedException {
		Food food = new Food();
		People[] peoples = new People[NUM];
		Monkey[] monkeys = new Monkey[NUM];
		for (int i = 0; i < NUM; i++) {
			peoples[i] = new People((i + 1) + "号人", food);
		}
		for (People p : peoples) {
			p.start();
		}
		Thread.sleep(1000 * 5);
		System.out.println();
		for (int i = 0; i < NUM; i++) {
			monkeys[i] = new Monkey((i + 1) + "号猴子", food);
		}
		for (Monkey m : monkeys) {
			m.start();
		}
	}


}


代码4-4运行结果:

1号人等待面包
5号人等待面包
4号人等待面包
3号人等待面包
2号人等待面包
1号人得到面包  
5号人得到面包  
4号人得到面包  
3号人得到面包  
2号人得到面包  


1号猴子等待香蕉
1号猴子得到香蕉  
4号猴子等待香蕉
4号猴子得到香蕉  
3号猴子等待香蕉
2号猴子等待香蕉
5号猴子等待香蕉
3号猴子得到香蕉  
2号猴子得到香蕉  
5号猴子得到香蕉  


方法 int getHoldCount()的作用是查询当前线程保持此锁定的个数,也是调用lock()方法的次数

代码4-5

import java.util.concurrent.locks.ReentrantLock;

public class HoldCount {
	public ReentrantLock lock = new ReentrantLock();

	public void mothodA() {
		lock.lock();
		System.out.println("A方法");
		System.out.println("hold count:" + lock.getHoldCount());
		mothodB();
		lock.unlock();
	}

	public void mothodB() {
		lock.lock();
		System.out.println("B方法");
		System.out.println("hold count:" + lock.getHoldCount());
		lock.unlock();
	}

	public int getHoldCount() {
		return lock.getHoldCount();
	}

	public static void main(String[] args) {
		HoldCount count = new HoldCount();
		count.mothodA();
		count.mothodB();
		System.out.println("hold count:" + count.getHoldCount());
	}
}

代码4-5运行结果:

A方法
hold count:1
B方法
hold count:2
B方法
hold count:1
hold count:0


方法 int getQueueLength()的作用是返回正在等待获取此锁定的线程估计数,比如有六个线程,其中一个获得了锁并因为其他原因陷入等待,那么调用getQueueLength()方法后将返回5

代码4-6

import java.util.concurrent.locks.ReentrantLock;

public class QueueLength {

	private ReentrantLock lock = new ReentrantLock();

	public void sleep() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "获得锁");
			Thread.sleep(Integer.MAX_VALUE);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public int getQueueLength() {
		return lock.getQueueLength();
	}

	public static void main(String[] args) throws InterruptedException {
		final QueueLength queueLength = new QueueLength();
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				queueLength.sleep();
			}
		};
		for (int i = 0; i < 6; i++) {
			new Thread(runnable).start();
		}
		Thread.sleep(100);
		System.out.println(queueLength.getQueueLength());
	}

}

代码4-6运行结果:

Thread-0获得锁
5


方法 int getWaitQueueLength(Condition condition)的作用是返回等待与此锁相关的给定条件Condition的线程估计数,比如有6个线程,每个线程获取锁后执行await()方法后,调用getWaitQueueLength(Condition condition)返回6

方法boolean hasWaiters(Condition condition)的作用是查询是否有线程正在等待与此锁定有关的condition条件

代码4-7

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;


public class ConditionAwait {


	private ReentrantLock lock = new ReentrantLock();
	private Condition conditionA = lock.newCondition();
	private Condition conditionB = lock.newCondition();


	public void awaitA() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "等待conditionA");
			conditionA.await();
			System.out.println(Thread.currentThread().getName() + "等待结束");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}


	public void awaitB() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "等待conditionB");
			conditionB.await();
			System.out.println(Thread.currentThread().getName() + "等待结束");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}


	public void signal() {
		lock.lock();
		conditionA.signal();
		conditionB.signal();
		lock.unlock();
	}


	public void waitQueueLength() {
		lock.lock();
		if (lock.getWaitQueueLength(conditionA) != 0) {
			System.out.println("有" + lock.getWaitQueueLength(conditionA) + "等待conditionA");
		}
		if (lock.getWaitQueueLength(conditionB) != 0) {
			System.out.println("有" + lock.getWaitQueueLength(conditionB) + "等待conditionB");
		}
		lock.unlock();
	}


	public void hasWaiters() {
		lock.lock();
		System.out.println("是否有线程在等待conditionA:" + lock.hasWaiters(conditionA));
		System.out.println("是否有线程在等待conditionB:" + lock.hasWaiters(conditionB));
		lock.unlock();
	}


	public static void main(String[] args) throws InterruptedException {
		final ConditionAwait await = new ConditionAwait();
		Runnable runnableA = new Runnable() {


			@Override
			public void run() {
				await.awaitA();
			}
		};


		Runnable runnableB = new Runnable() {


			@Override
			public void run() {
				await.awaitB();
			}
		};
		for (int i = 0; i < 3; i++) {
			new Thread(runnableA).start();
		}
		for (int i = 0; i < 5; i++) {
			new Thread(runnableB).start();
		}
		Thread.sleep(100);
		await.hasWaiters();
		System.out.println("——————————线程等待统计————————-—");
		await.waitQueueLength();
		System.out.println("——————————开始唤醒线程————————-—");
		for (int i = 0; i < 5; i++) {
			await.waitQueueLength();
			Thread.sleep(100);
			await.signal();
		}
		Thread.sleep(100);
		await.hasWaiters();
	}


}



代码4-7运行结果:

Thread-0等待conditionA
Thread-1等待conditionA
Thread-2等待conditionA
Thread-3等待conditionB
Thread-4等待conditionB
Thread-5等待conditionB
Thread-6等待conditionB
Thread-7等待conditionB
是否有线程在等待conditionA:true
是否有线程在等待conditionB:true
——————————线程等待统计————————-—
有3等待conditionA
有5等待conditionB
——————————开始唤醒线程————————-—
有3等待conditionA
有5等待conditionB
有2等待conditionA
有4等待conditionB
Thread-0等待结束
Thread-3等待结束
有1等待conditionA
有3等待conditionB
Thread-1等待结束
Thread-4等待结束
有2等待conditionB
Thread-2等待结束
Thread-5等待结束
有1等待conditionB
Thread-6等待结束
Thread-7等待结束
是否有线程在等待conditionA:false
是否有线程在等待conditionB:false


方法boolean hasQueuedThread(Thread thread)的作用是查询指定的线程是否正在等待此锁定

方法boolean hasQueuedThreads()的作用是查询是否有线程正在等待获取此锁定

代码4-8

import java.util.concurrent.locks.ReentrantLock;

public class HasQueueThread {

	private ReentrantLock lock = new ReentrantLock();

	public void sleep() {
		try {
			lock.lock();
			Thread.sleep(Integer.MAX_VALUE);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public boolean hasQueuedThreads() {
		return lock.hasQueuedThreads();
	}

	public boolean hasQueuedThread(Thread thread) {
		return lock.hasQueuedThread(thread);
	}

	public static void main(String[] args) throws InterruptedException {
		final HasQueueThread hasQueueThread = new HasQueueThread();
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				hasQueueThread.sleep();
			}
		};
		Thread[] threads = new Thread[6];
		for (int i = 0; i < 6; i++) {
			threads[i] = new Thread(runnable);
		}
		for (Thread thread : threads) {
			thread.start();
		}
		Thread.sleep(100);
		System.out.println("是否有线程正在等待锁定:" + hasQueueThread.hasQueuedThreads());
		for (Thread thread : threads) {
			if (!hasQueueThread.hasQueuedThread(thread)) {
				System.out.println(thread.getName() + "取得锁定");

			} else {
				System.out.println(thread.getName() + "等待锁定");
			}
		}

	}

}

代码4-8运行结果:

是否有线程正在等待锁定:true
Thread-0取得锁定
Thread-1等待锁定
Thread-2等待锁定
Thread-3等待锁定
Thread-4等待锁定
Thread-5等待锁定


方法boolean hasWaiters(Condition condition)的作用是查询是否有线程正在等待此锁相关的condition条件


方法boolean isFair()的作用是判断是否是公平锁

方法boolean isHeldByCurrentThread()的作用是查询当前线程是否保持此锁定

boolean isLocked()查询此锁定是否由任意线程保持

代码4-9

import java.util.concurrent.locks.ReentrantLock;

public class ThreadLock {

	private ReentrantLock fair = new ReentrantLock(true);
	private ReentrantLock unfair = new ReentrantLock(false);// 默认情况下,ReentrantLock使用非公平锁

	public void showFair() {
		fair.lock();
		System.out.println("fair公平锁情况:" + fair.isFair());
		fair.unlock();
	}

	public void showUnFair() {
		fair.lock();
		System.out.println("unfair公平锁情况:" + unfair.isFair());
		fair.unlock();
	}

	public void isHeldByCurrentThread() {
		System.out.println("lock()前," + Thread.currentThread().getName() + "是否保持此锁定:" + fair.isHeldByCurrentThread());
		fair.lock();
		System.out.println("lock()后,unlock()前," + Thread.currentThread().getName() + "是否保持此锁定:" + fair.isHeldByCurrentThread());
		fair.unlock();
		System.out.println("unlock()后," + Thread.currentThread().getName() + "是否保持此锁定:" + fair.isHeldByCurrentThread());
	}

	public void keepLock() {
		try {
			unfair.lock();
			Thread.sleep(Integer.MAX_VALUE);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			unfair.unlock();
		}
	}

	public void isLocked() {
		System.out.println("unfair是否保持锁定:" + unfair.isLocked());
	}

	public static void main(String[] args) throws InterruptedException {
		final ThreadLock lock = new ThreadLock();
		Runnable runnableA = new Runnable() {
			@Override
			public void run() {
				lock.showFair();
			}
		};
		new Thread(runnableA).start();
		Runnable runnableB = new Runnable() {
			@Override
			public void run() {
				lock.showUnFair();
			}
		};
		new Thread(runnableB).start();

		Runnable runnableC = new Runnable() {
			@Override
			public void run() {
				lock.isHeldByCurrentThread();
			}
		};
		new Thread(runnableC).start();

		Runnable runnableD = new Runnable() {
			@Override
			public void run() {
				lock.keepLock();
			}
		};
		for (int i = 0; i < 3; i++) {
			new Thread(runnableD).start();
		}
		Thread.sleep(100);
		lock.isLocked();
		System.exit(0);
	}

}

代码4-9运行结果:

fair公平锁情况:true
unfair公平锁情况:false
lock()前,Thread-2是否保持此锁定:false
lock()后,unlock()前,Thread-2是否保持此锁定:true
unlock()后,Thread-2是否保持此锁定:false
unfair是否保持锁定:true


方法void lockInterruptibly()作用与void lock()相似,但如果线程执行的时候被中断会抛出异常

代码4-10

import java.util.concurrent.locks.ReentrantLock;

public class LockInterruptibly {

	private ReentrantLock lock = new ReentrantLock();

	public void threadWait() {
		int count = 0;
		try {
			lock.lockInterruptibly();
			System.out.println(Thread.currentThread().getName() + "获取锁定");
			while (count < Integer.MAX_VALUE / 100) {
				count++;
				Math.random();
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			System.out.println(Thread.currentThread().getName() + "中断异常");
			e.printStackTrace();
		} finally {
			if (lock.isHeldByCurrentThread()) {
				System.out.println(Thread.currentThread().getName() + "解除锁定");
				lock.unlock();
			}
		}

	}

	public static void main(String[] args) throws InterruptedException {
		final LockInterruptibly interruptibly = new LockInterruptibly();
		Runnable runnable = new Runnable() {

			@Override
			public void run() {
				interruptibly.threadWait();
			}
		};
		new Thread(runnable).start();
		Thread.sleep(3000);
		Thread thread = new Thread(runnable);
		thread.start();
		thread.interrupt();

	}

}

代码4-10运行结果:

Thread-0获取锁定
Thread-0解除锁定
Thread-1中断异常
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1219)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
at com.jerry.ch4.LockInterruptibly.threadWait(LockInterruptibly.java:12)
at com.jerry.ch4.LockInterruptibly$1.run(LockInterruptibly.java:37)
at java.lang.Thread.run(Thread.java:722)



方法boolean tryLock()尝试是否能获得锁 如果不能获得立即返回

方法boolean tryLock(long timeout, TimeUnit unit)在指定时间内尝试是否能获得锁,如果不能获得立即返回

代码4-11

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;


public class TryLock {


	private ReentrantLock lock = new ReentrantLock();


	public void tryLockA() {
		boolean requireLock = false;
		if (lock.tryLock()) {
			requireLock = true;
			System.out.println(Thread.currentThread().getName() + "获得锁定");
		} else {
			System.out.println(Thread.currentThread().getName() + "没有获得锁定");
		}
		if (requireLock) {
			lock.unlock();
		}
	}


	public void tryLockB() {
		boolean requireLock = false;
		try {
			if (lock.tryLock(2, TimeUnit.SECONDS)) {
				requireLock = true;
				System.out.println(Thread.currentThread().getName() + "获得锁定");
			} else {
				System.out.println(Thread.currentThread().getName() + "没有获得锁定");
			}
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (requireLock) {
				lock.unlock();
			}
		}


	}


	public static void main(String[] args) throws InterruptedException {
		final TryLock lock = new TryLock();
		Runnable runnableA = new Runnable() {
			@Override
			public void run() {
				lock.tryLockA();
			}
		};
		new Thread(runnableA).start();
		Thread.sleep(100);
		new Thread(runnableA).start();
		new Thread(runnableA).start();
		Thread.sleep(100);
		Runnable runnableB = new Runnable() {
			@Override
			public void run() {
				lock.tryLockB();
			}
		};
		new Thread(runnableB).start();
		Thread.sleep(3000);
		new Thread(runnableB).start();
		new Thread(runnableB).start();
	}


}



代码4-11运行结果:

Thread-0获得锁定
Thread-1获得锁定
Thread-2获得锁定
Thread-3获得锁定
Thread-3进入finally
Thread-4获得锁定
Thread-4进入finally
Thread-5没有获得锁定
Thread-5进入finally


方法void awaitUninterruptibly()可以让线程陷入等待状态,但执行线程中断方法时该线程不会中断

代码4-12

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class AwaitUninterruptibly {
	private ReentrantLock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();

	public void await() {
		try {
			lock.lock();
			System.out.println("await begin");
			condition.await();
			System.out.println("await end");
		} catch (InterruptedException e) {
			System.out.println("catch InterruptedException");
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void awaitUninterruptibly() {
		lock.lock();
		System.out.println("awaitUninterruptibly begin");
		condition.awaitUninterruptibly();
		System.out.println("awaitUninterruptibly end");
		lock.unlock();
	}

	public static void main(String[] args) throws InterruptedException {
		final AwaitUninterruptibly uninterruptibly = new AwaitUninterruptibly();
		Runnable runnableA = new Runnable() {
			@Override
			public void run() {
				uninterruptibly.await();
			}
		};
		Thread threadA = new Thread(runnableA);
		threadA.start();
		Thread.sleep(300);
		threadA.interrupt();
		Thread.sleep(300);
		Runnable runnableB = new Runnable() {
			@Override
			public void run() {
				uninterruptibly.awaitUninterruptibly();
			}
		};
		Thread threadB = new Thread(runnableB);
		threadB.start();
		Thread.sleep(300);
		threadB.interrupt();
	}

}

代码4-12运行结果:

await begin
catch InterruptedException
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2017)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2052)
at com.jerrych4.AwaitUninterruptibly.await(AwaitUninterruptibly.java:14)
at com.jerrych4.AwaitUninterruptibly$1.run(AwaitUninterruptibly.java:37)
at java.lang.Thread.run(Thread.java:745)
awaitUninterruptibly begin


日期转换工具

代码4-13

import java.text.ParseException;
import java.util.Date;

public class DateUtils {
	public static String DateToString(Date date, String toFormatStr) {
		try {
			return new java.text.SimpleDateFormat(toFormatStr).format(date);
		} catch (Exception e) {
			e.printStackTrace();
			return "";
		}
	}

	public static Date StringToDate(String dateString, String strFormat) {
		try {
			Date date = new java.text.SimpleDateFormat(strFormat).parse(dateString);
			return date;
		} catch (ParseException e) {
			e.printStackTrace();
			return null;
		}
	}
}


方法boolean awaitUntil(Date deadline)可以使得线程等待,在指定日期唤醒自己,但如果相关条件condition提前唤醒,则不会等到日期到了再唤醒自己

代码4-14

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class AwaitUntil {

	private ReentrantLock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();

	public void awaitUntil() {
		try {
			Calendar calendar = Calendar.getInstance();
			calendar.add(Calendar.SECOND, 10);
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "开始时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
			condition.awaitUntil(calendar.getTime());
			System.out.println(Thread.currentThread().getName() + "结束时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void singalAll() {
		lock.lock();
		System.out.println("唤醒开始时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
		condition.signalAll();
		System.out.println("唤醒结束时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
		lock.unlock();
	}

	public static void main(String[] args) throws InterruptedException {
		final AwaitUntil until = new AwaitUntil();
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				until.awaitUntil();
			}
		};
		Thread threadA = new Thread(runnable);
		threadA.start();
		Thread.sleep(1000 * 15);
		Thread threadB = new Thread(runnable);
		threadB.start();
		Thread.sleep(100);
		until.singalAll();
	}

}

代码4-14运行结果:

Thread-0开始时间:2016-11-23 20:58:51
Thread-0结束时间:2016-11-23 20:59:01
Thread-1开始时间:2016-11-23 20:59:06
唤醒开始时间:2016-11-23 20:59:06
唤醒结束时间:2016-11-23 20:59:06
Thread-1结束时间:2016-11-23 20:59:06


使用ReentrantReadWriteLock类

类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()后的任务。这样做虽然保证了实例变量的线程安全性,但效率却非常低下所以,使用读写锁ReentrantReadWriteLock类,可以加快运行效率

读写锁表示有两个锁,一个是读操作相关的锁,也称为共享锁,另一个是写相关锁,也叫排它锁。也就是多个线程读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。


代码4-15

import java.util.Date;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLock {

	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

	public void read() {
		try {
			lock.readLock().lock();
			System.out.println(Thread.currentThread().getName() + "获取读锁时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			System.out.println(Thread.currentThread().getName() + "释放读锁时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
			lock.readLock().unlock();
		}
	}

	public void write() {
		try {
			lock.writeLock().lock();
			System.out.println(Thread.currentThread().getName() + "获取写锁时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			System.out.println(Thread.currentThread().getName() + "释放写锁时间:" + DateUtils.DateToString(new Date(), "yyyy-MM-dd HH:mm:ss"));
			lock.writeLock().unlock();
		}
	}

	public static void main(String[] args) throws InterruptedException {
		final ReadWriteLock lock = new ReadWriteLock();
		Runnable runnableA = new Runnable() {
			@Override
			public void run() {
				lock.read();
			}
		};
		System.out.println("——————————————读读共享——————————————");
		for (int i = 0; i < 3; i++) {
			new Thread(runnableA).start();
		}
		Thread.sleep(2000);
		System.out.println("——————————————读写互斥——————————————");
		Runnable runnableB = new Runnable() {
			@Override
			public void run() {
				lock.write();
			}
		};
		for (int i = 0; i < 3; i++) {
			new Thread(runnableB).start();
			new Thread(runnableA).start();
		}

	}

}

代码4-15运行结果:

——————————————读读共享——————————————
Thread-2获取读锁时间:2016-11-24 09:49:10
Thread-1获取读锁时间:2016-11-24 09:49:10
Thread-0获取读锁时间:2016-11-24 09:49:10
Thread-0释放读锁时间:2016-11-24 09:49:11
Thread-2释放读锁时间:2016-11-24 09:49:11
Thread-1释放读锁时间:2016-11-24 09:49:11
——————————————读写互斥——————————————
Thread-4获取读锁时间:2016-11-24 09:49:12
Thread-4释放读锁时间:2016-11-24 09:49:13
Thread-3获取写锁时间:2016-11-24 09:49:13
Thread-3释放写锁时间:2016-11-24 09:49:14
Thread-5获取写锁时间:2016-11-24 09:49:14
Thread-5释放写锁时间:2016-11-24 09:49:15
Thread-8获取读锁时间:2016-11-24 09:49:15
Thread-6获取读锁时间:2016-11-24 09:49:15
Thread-6释放读锁时间:2016-11-24 09:49:16
Thread-8释放读锁时间:2016-11-24 09:49:16
Thread-7获取写锁时间:2016-11-24 09:49:16
Thread-7释放写锁时间:2016-11-24 09:49:17

























  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值