6 实现 Callable 接口
Java 5.0 在 java.util.concurrent 提供了一个新的创建执行线程的方式:Callable 接口
Callable 需要依赖FutureTask ,FutureTask 也可以用作闭锁
6.1 创建线程的四种方式
无返回
- 实现Runnable接口,重写run()
- 继承Thread类,重写run()
有返回
- 实现Callable接口,重写call(),利用FutureTask包装Callable,并作为task传入Thread构造函数
-
6.2 Callable的使用
创建执行线程的方式三:实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常
- 执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future 接口的实现类
```java
class ThreadDemo implements Callable
{ @Override public Integer call() throws Exception {
} }int sum = 0;for (int i = 0; i <= 100000; i++) {sum += i;}return sum;
public class TestCallable {
public static void main(String[] args) {ThreadDemo td = new ThreadDemo();// 1.执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。FutureTask<Integer> result = new FutureTask<>(td);new Thread(result).start();// 2.接收线程运算后的结果try {Integer sum = result.get(); // FutureTask 可用于 闭锁System.out.println(sum);System.out.println("------------------------------------");} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}
}
---<a name="3d06b53a"></a>## 7 -Lock 同步锁在 Java 5.0 之前,协调共享对象的访问时可以使用的机制只有 synchronized 和 volatile 。Java 5.0 后增加了一些新的机制,但并不是一种替代内置锁的方法,而是当内 置锁不适用时,作为一种可选择的高级功能。<br />ReentrantLock 实现了 Lock 接口,并提供了与 synchronized 相同的互斥性和内存可见性。但相较于synchronized 提供了更高的处理锁的灵活性。<br />解决多线程安全问题的三种方式- jdk 1.5 前- synchronized:隐式锁<br />1.同步代码块<br />2.同步方法- jdk 1.5 后- 3.同步锁 Lock:显式锁<br />注意:是一个显示锁,需要通过 lock() 方法上锁,必须通过 unlock() 方法进行释放锁```javapublic class TestLock {public static void main(String[] args) {Ticket ticket = new Ticket();new Thread(ticket, "1号窗口").start();new Thread(ticket, "2号窗口").start();new Thread(ticket, "3号窗口").start();}}class Ticket implements Runnable {private int tick = 100;private Lock lock = new ReentrantLock();@Overridepublic void run() {while(true) {lock.lock(); // 上锁try {if(tick > 0) {try {Thread.sleep(200);} catch (InterruptedException e) {}System.out.println(Thread.currentThread().getName()+ " 完成售票,余票为:" + --tick);}}finally{lock.unlock(); // 必须执行 因此放在finally中 释放锁}}}}
8 Condition 控制线程通信
Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版本中的不同。
在 Condition 对象中,与 wait、notify 和 notifyAll 方法对应的分别是 await、signal 和 signalAll
Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。
8.1 使用Condition
使用Condition控制线程通信
- 如果不使用 synchronized 关键字保证同步,而是直接使用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也就不能使用 wait() notify() notifyAll() 来进行线程通信了
- 当使用 lock 对象来保证同步时,Java提供了一个 Condition 类来保持协调,使用Condition可以让那些已经得到 lock 对象却无法继续执行的线程释放lock对象,Condition对象也可以唤醒其他处于等待状态的进程。
Condition 实例被绑定在一个 Lock 对象上。要获得 Lock 实例的 Condition 实例,调用 Lock 对象的newCondition() 方法即可
8.2 生产者和消费者案例
使用synchronized方式 ```java public class TestProductorAndConsumer { public static void main(String[] args) {
Clerk clerk = new Clerk();Productor productor = new Productor(clerk);Consumer consumer = new Consumer(clerk);new Thread(productor, "生产者A").start();new Thread(consumer, "消费者B").start();new Thread(productor, "生产者C").start();new Thread(consumer, "消费者D").start();
} }
class Clerk { // 店员 private int product = 0;
public synchronized void get() { // 进货while (product >= 1) { // 为了避免虚假唤醒问题,应该总是使用在循环中而不用ifSystem.out.println("产品已满!");try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + " : " + ++product);this.notifyAll();}public synchronized void sale() { // 销售while (product <= 0) { // 为了避免虚假唤醒问题,应该总是使用在循环中System.out.println("缺货!");try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + " : " + --product);this.notifyAll();}
}
class Productor implements Runnable { // 生产者 private Clerk clerk;
public Productor(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}clerk.get();}}
}
class Consumer implements Runnable { // 消费者 private Clerk clerk;
public Consumer(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {clerk.sale();}}
}
使用Lock方式```javapublic class TestProductorAndConsumer {public static void main(String[] args) {Clerk clerk = new Clerk();Productor productor = new Productor(clerk);Consumer consumer = new Consumer(clerk);new Thread(productor, "生产者A").start();new Thread(consumer, "消费者B").start();new Thread(productor, "生产者C").start();new Thread(consumer, "消费者D").start();}}class Clerk {//店员private int product = 0;private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();private Condition condition2 = lock.newCondition();public void get() { // 进货lock.lock();try {while (product >= 1) {System.out.println("产品已满!");try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + " : " + ++product);condition2.signalAll();} finally {lock.unlock();}}public void sale() { // 销售lock.lock();try {while (product <= 0) {System.out.println("缺货!");try {condition2.await();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + " : " + --product);condition.signalAll();} finally {lock.unlock();}}}class Productor implements Runnable { // 生产者private Clerk clerk;public Productor(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}clerk.get();}}}class Consumer implements Runnable { // 消费者private Clerk clerk;public Consumer(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {clerk.sale();}}}

8.3 线程按序交替
要求:编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,每个线程将自己的 ID 在屏幕上打印 10 遍,要 求输出的结果必须按顺序显示。 如:ABCABCABC…… 依次递归
public class TestABCAlternate {public static void main(String[] args) {AlternateDemo ad = new AlternateDemo();new Thread(new Runnable() {@Overridepublic void run() {for (int i = 1; i <= 10; i++) {ad.loopA(i);}}}, "A").start();new Thread(new Runnable() {@Overridepublic void run() {for (int i = 1; i <= 10; i++) {ad.loopB(i);}}}, "B").start();new Thread(new Runnable() {@Overridepublic void run() {for (int i = 1; i <= 10; i++) {ad.loopC(i);}}}, "C").start();}}class AlternateDemo {private int number = 1;Lock lock = new ReentrantLock();Condition condition1 = lock.newCondition();Condition condition2 = lock.newCondition();Condition condition3 = lock.newCondition();/*** @param totalLoop 循环第几轮*/public void loopA(int totalLoop) {lock.lock();try {if (number != 1) {condition1.await();}for (int i = 1; i <= 1; i++) {System.out.println(Thread.currentThread().getName()+ "\t" + i + "\t" + totalLoop);}number = 2;condition2.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void loopB(int totalLoop) {lock.lock();try {if (number != 2) {condition2.await();}for (int i = 1; i <= 1; i++) {System.out.println(Thread.currentThread().getName()+ "\t" + i + "\t" + totalLoop);}number = 3;condition3.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void loopC(int totalLoop) {lock.lock();try {if (number != 3) {condition3.await();}for (int i = 1; i <= 1; i++) {System.out.println(Thread.currentThread().getName()+ "\t" + i + "\t" + totalLoop);}number = 1;condition1.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}

9 ReadWriteLock 读写锁
ReadWriteLock 维护了一对相关的锁,一个用于只读操作, 另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的
ReadWriteLock 读取操作通常不会改变共享资源,但执行写入操作时,必须独占方式来获取锁。对于读取操作占多数的数据结构。 ReadWriteLock 能提供比独占锁更高的并发性。而对于只读的数据结构,其中包含的不变性 可以完全不需要考虑加锁操作
- 写写/读写 需要“互斥”
读读 不需要互斥 ```java public class TestReadWriteLock { public static void main(String[] args) {
ReadWriteLockDemo rw = new ReadWriteLockDemo();new Thread(new Runnable() {@Overridepublic void run() {rw.set((int)(Math.random() * 101));}}, "write").start();for (int i = 0; i < 100; i++) {new Thread(new Runnable() {@Overridepublic void run() {rw.get();}}).start();}
} }
class ReadWriteLockDemo { private int number = 0; private ReadWriteLock lock = new ReentrantReadWriteLock();
public void get() { // 读lock.readLock().lock(); // 上锁try {System.out.println(Thread.currentThread().getName() + " : " + number);} catch (Exception e) {e.printStackTrace();} finally {lock.readLock().unlock(); // 释放锁}}public void set(int number) {lock.writeLock().lock();try {System.out.println(Thread.currentThread().getName());this.number = number;} catch (Exception e) {e.printStackTrace();} finally {lock.writeLock().unlock();}}
}
---<a name="e1202f16"></a>## 10 线程8锁判断打印的 "one" or "two" ?1. 两个普通同步方法,两个线程,标准打印, 打印结果?1. 新增 Thread.sleep() 给 getOne(),打印结果?1. 新增普通方法 getThree() , 打印结果?1. 两个普通同步方法,两个 Number 对象,打印结果?1. 修改 getOne() 为静态同步方法,打印结果?1. 修改两个方法均为静态同步方法,一个 Number 对象,打印结果?1. 一个静态同步方法,一个非静态同步方法,两个 Number 对象,打印结果?1. 两个静态同步方法,两个 Number 对象,打印结果?要想知道上面线程8锁的答案,需要知晓关键所在- ① 非静态方法的锁默认为 this(实例对象), 静态方法的锁为对应的 Class 对象(类对象)- ② 某一个时刻,同一个对象,只能有一个线程持有锁,无论几个方法- ③ 锁静态方法,某一个时刻,不同实例对象也只能有一个对象持有锁```javapublic class TestThread8Monitor {public static void main(String[] args) {Number number = new Number();Number number2 = new Number();new Thread(new Runnable() {@Overridepublic void run() {number.getOne();}}).start();new Thread(new Runnable() {@Overridepublic void run() {// number.getTwo();number2.getTwo();}}).start();// new Thread(new Runnable() {// @Override// public void run() {// number.getThree();// }// }).start();}}class Number {public static synchronized void getOne() { // Number.classtry {Thread.sleep(3000);} catch (InterruptedException e) {}System.out.println("one");}public synchronized void getTwo() { // thisSystem.out.println("two");}public void getThree() {System.out.println("three");}}
答案
- 两个普通同步方法,两个线程,一个 Number 对象,标准打印, 打印结果? //one two
- 新增 Thread.sleep() 给 getOne() ,打印结果? // —过了3秒— one two
- 新增普通方法 getThree() , 打印结果? //three —过了3秒— one two
- 两个普通同步方法,两个 Number 对象,打印结果? //two —过了3秒— one
- 修改 getOne() 为静态同步方法,打印结果? //two —过了3秒— one
- 修改两个方法均为静态同步方法,一个 Number 对象,打印结果? //—过了3秒— one two
- 一个静态同步方法,一个非静态同步方法,两个 Number 对象,打印结果? //two —过了3秒— one
- 两个静态同步方法,两个 Number 对象,打印结果? //—过了3秒— one two
