第九章小结
1.FutureTask类有什么作用?它实现了哪些接口? Callable接口和Runnable接口有什么不同?
Future Task 类: 在java中,一般是通过继承Thread 类 或者实现Runable 接口来创建多线程,Runable 接口不能返回结果,如果要获取子线程的执行结果,一般都是在子线程执行结束后,通过Handler 将结果返回到调用线程,jdk1.5 之后,Java提供了Callable 接口来封装子任务,Callable接口可以获取返回结果。
接口:
返回值 | 方法名称 | 方法用途 |
---|---|---|
boolean | cancel(boolean mayInterruptlfRunning | 尝试取消此任务的执行。如果任务已经完成、已经被取消或由于其他原因无法取消,则此尝试将失败。如果成功,并且在调用cancel时该任务没有启动,则该任务永远不应该运行。如果该任务已经启动,则mayInterruptIfRunning参数确定执行该任务的线程是否应该中断以试图停止该任务 |
boolean | isCancelled() | 如果此任务在正常完成之前被取消,则返回true |
boolean | isDone | 如果此任务完成则返回true |
V | get() | 获取线程执行结果,会一直等待,直到线程执行完成 |
V | get(long timeout,TimeUnit unit) | 获取线程执行结果,如果在指定时间内没有获取到结果则会抛出异常 |
Callable接口和Runnable接口的不同:
Callable接口和Runnable接口 相似,区别就是Callable 需要实现call方法,而Runnable 需要实现run方法;并且,call方法还可以返回任何对象,无论是什么对象,JVM都会当做Object 来处理。但是如果使用了泛型,我们就不用每次都对Object进行转换了。
不同之处:
- Callable 可以返回一个类型V,热Runable 不可以
- Callable 能够抛出checked exception,而Runable不可以
- Runable 自从java1.1 就有了,而Callable 是1.5之后才加上去的。
- Callable 和 Runable 都可以应用于executors。而Thread类只支持Runable
2.请查阅JDK自学线程池的相关类,如ThreadPoolExecutor构造器各个参数的意义, 利用线程池编写多线程程序。
ThreadPoolExecutor :类是java的线程池实现的核心类,ThreadPoolExecutor类有四个构造方法,其中三个构造方法最终都是调用另一个参数最全的构造方法。
- corePoolSIze: 核心线程池的大小
- maximumPoolSzie: 线程池中最大的存活线程数。
- **keepAliveTime:**当线程数大于核心数时,这是多余空闲线程在终止前等待新任务的最长时间。
- unit: keepAliveTime参数的时间单位
- **workQueue:**用于在任务执行前保留任务的队列,此队列将仅包含execute方法提交的可运行任务。
- threadFactory: 线程工厂类。用于在需要的时候生成新的线程。
- handler: 这个参数的作用是当提交任务时既没有空闲线程,任务队列也满了,这时候就会调用handler 的rejectedExecution方法。
一句话:新任务------>corePoolSize满了 -----> workQueue满了---------> maximumPoolSize 满了-----> handler.rejectedExecution()执行拒绝策略
超出了corePoolSize创建的那些线程,会在空闲一定的时间后被收回,空闲的时间,就是由keepAliveTime + unit 控制的。
package Testcom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(2);// 包含2个线程对象
//创建Runnable实例对象
MyRunnable r = new MyRunnable();
//自己创建线程对象的方式
//Thread t = new Thread(r);
//t.start(); ---> 调用MyRunnable中的run()
//从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(r);
//再获取个线程对象,调用MyRunnable中的run()
service.submit(r);
service.submit(r);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
//关闭线程池
//service.shutdown();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教练来了: " + Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
}
}
/*
运行结果:
我要一个教练
我要一个教练
教练来了: pool-1-thread-1
教我游泳,交完后,教练回到了游泳池
教练来了: pool-1-thread-2
教我游泳,交完后,教练回到了游泳池
我要一个教练
教练来了: pool-1-thread-1
教我游泳,交完后,教练回到了游泳池
*/
3.volatile关键字有什么作用?
在JVM 1.2 之前,Java 总是从主存读取变量,但随着JVM 的优化,线程可以把主存变量保存在寄存器(工作内存)中操作,线程结束再与主存变量进行同步,然而,当线程没有执行结束就发生了互换这就可能造成一个线程在主存中修改了一个变量的值,而另一个线程还继续使用它在寄存器中变量值的副本,造成数据的不一致。要解决这个问题,就需要把该变量声明为volatile(不稳定),它指示JVM这个变量是不稳定的,每次使用它都到主存中进行读取,因此多线程环境下volatile 关键字的使用便得非常重要。一般来说,多线程环境下个线程间共享的变量都应该加volatile 修饰。
**volatile ** 禁用CPU缓存(可见性问题)
可见性:一个线程对共享变量的修改对其他线程都是可见的。
volatile修饰的变量:对此变量的操作都会在内存中进行,不会产生副本,以保证共享变量的可见性。
4.Java提供了哪些同步机制来实现互斥?
Java 提供了两种锁机制 来控制多个线程对共享资源的互斥访问,第一个是JVM 实现的synchronized 和对象监视器,而另一个是JDK 实现的ReentrantLock。
synchronized 的四种用法:
- synchronized 代码块:监视器就是指定的对象
class Resource implements Runnable {
volatile public int i;
volatile public Integer it;
public Resource(int _i){
i = _i;
it = new Integer(i);
}
public void run(){
while(true){
synchronized(it){
if (i>0){
try{
Thread.sleep(200);
}
catch(Exception e){}
i--;
System.out.println(Thread.
currentThread().getName()+" "+i);
}
else{
System.out.println(Thread.
currentThread().getName());
break; }}
}
}
}
public class TestSecurity{
public static void main(String[] args){
Resource m = new Resource(9);
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
t1.start();
t2.start();
}
}
//运行结果:
/*
Thread-0 8
Thread-0 7
Thread-0 6
Thread-0 5
Thread-0 4
Thread-0 3
Thread-0 8
Thread-0 2
Thread-1 1
Thread-0 0
Thread-0
Thread-0 1
*/
同步块又称为临界块,保证同一时间只有一个线程执行同步块内的代码(互斥)
- synchronized 方法:监视器就是this 对象
class Resource implements Runnable {
volatile public int i;
public Resource(int _i){
i = _i;
}
public synchronized void run(){
while(true){
if (i>0){
try{
Thread.sleep(200);
}
catch(Exception e){}
i--;
System.out.println(Thread.
currentThread().getName()+" "+i);
}
else{
System.out.println(Thread.
currentThread().getName());
break;
}}
}
}
public class TestSecurity{
public static void main(String[] args){
Resource m = new Resource(9);
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
t1.start();
t2.start();
}
}
//运行结果:
/*
Thread-0 8
Thread-0 7
Thread-0 6
Thread-0 5
Thread-0 4
Thread-0 3
Thread-0 8
Thread-0 2
Thread-0 1
Thread-0 0
Thread-0
Thread-0 1
*/
锁的范围尽可能小:能锁对象,就不要锁类;能锁代码块,就不要锁方法。
- synchronized 静态方法:监视器就是想要的Class 对象
- synchronized 类
public class SynchronizedExample {
public void func2() {
synchronized (SynchronizedExample.class) {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
}
}
}
public static void main(String[] args) {
SynchronizedExample e1 = new SynchronizedExample();
SynchronizedExample e2 = new SynchronizedExample();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> e1.func2());
executorService.execute(() -> e2.func2());
}
/*
结果:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
*/
ReentrantLock :java.uti.concurrent 包中的锁
public class LockExample {
private Lock lock = new ReentrantLock();
public void func() {
lock.lock();
try {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
} finally {
lock.unlock(); // 确保释放锁,从而避免发生死锁。
}
}
}
public static void main(String[] args) {
LockExample lockExample = new LockExample();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> lockExample.func());
executorService.execute(() -> lockExample.func());
}
/*
结果:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
*/
两者比较:
- 1.synchronized 是JVM 实现的,而ReentrankLock 是JDK实现的。
- 2.新版本 java 对synchronized 进行了很多优化,synchronized 和 ReentrankLock 大致相同
- 3.当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。ReentrankLock 可以中断,而synchronized 不行
- 4.公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。synchronized 中的锁是非公平的,ReentrankLock 默认情况下也是非公平的,但是也可以是公平的。
- 5.一个ReentrankLock 可以同时绑定多个Condition 对象。
使用选择: 除非需要使用ReentrankLock 的高级功能,否则优先使用synchronized。这是因为synchronized 是JVM 实现的一种锁机制,JVM原生地支持它,而ReentrankLock 不是所有的JDK 版本都支持。并且使用synchronized 不用担心没有释放锁而导致死锁问题,因为JVM会确保锁的释放。
5.编写Java程序模拟烧水泡茶最优工序。
工作:洗水壶(1分钟)
洗茶壶(1分钟)
洗茶杯(2分钟)
拿茶叶(1分钟)
烧开水(15分钟)
泡茶
如何安排合理的工序?
提高工作效率
缩短工时
package Testcom;
class xiShuihu extends Thread {
private Integer i;
xiShuihu(Integer i) {
this.i = i;
}
public synchronized void run() {
int ww = 0;
while (++ww <= 1) {
i++;
int w = 0;
while (++w <= 1000);
System.out.println("xishuihu--" + i);
}
}
}
class shaoshui implements Runnable {
private Integer i;
public int ww = 0;
shaoshui(Integer i) {
this.i = i;
}
@Override
public void run() {
// TODO Auto-generated method stub
while (++ww <= 15) {
i++;
int w = 1;
while (w++ <= 1000)
;
System.out.println("shaoshui--" + i);
}
}
}
class cleanAndTake implements Runnable {
private Integer i;
public int ww = 0;
cleanAndTake(Integer i) {
this.i = i;
}
@Override
public void run() {
// TODO Auto-generated method stub
while (++ww <= 4) {
i++;
int w = 0;
while (++w <= 1000)
;
System.out.println("clean and take -- " + i);
}
}
}
class paocha implements Runnable{
private Integer i;
paocha(Integer i) {
this.i = i;
}
@Override
public void run() {
// TODO Auto-generated method stub
int m = 0;
while(++m<=10) {
i++;
int w = 0;
while(++w<=1000);
System.out.println("paocha--" + i);
}
}
}
public class Zhu {
public static void main(String[] args) {
Thread xsh = new xiShuihu(0);
xsh.start();
try {
xsh.join();
}
catch(InterruptedException it) {}
shaoshui r = new shaoshui(0);
Thread sh = new Thread(r);
cleanAndTake r1 = new cleanAndTake(0);
Thread cat = new Thread(r1);
sh.start();
cat.start();
try {
sh.join();
}
catch(InterruptedException it) {}
try {
cat.join();
}
catch(InterruptedException it) {}
paocha r2 = new paocha(0);
Thread pc = new Thread(r2);
pc.start();
}
}
/*
运行结果:
xishuihu--1
shaoshui--1
shaoshui--2
clean and take -- 1
shaoshui--3
clean and take -- 2
shaoshui--4
shaoshui--5
shaoshui--6
clean and take -- 3
shaoshui--7
shaoshui--8
shaoshui--9
clean and take -- 4
shaoshui--10
shaoshui--11
shaoshui--12
shaoshui--13
shaoshui--14
shaoshui--15
paocha--1
paocha--2
paocha--3
paocha--4
paocha--5
paocha--6
paocha--7
paocha--8
paocha--9
paocha--10
*/
6.请使用Java并发包的Lock及Conditon改写例9.11。
// 例9.11
class Account
{
volatile private int value;
volatile private boolean isMoney=false;
synchronized void put(int i)
{
if(isMoney)
{
try{wait();}
catch(Exception e){}
}
value = value + i;
System.out.println("存入"+i+" 账上金额为:"+value);
isMoney =true;
notify();
}
synchronized int get(int i)
{
if(!isMoney)
{
try{wait();}
catch(Exception e){}
}
if (value>i)
value = value - i;
else
{ i = value;
value = 0;
}
System.out.println("取走"+i+" 账上金额为:"+value);
isMoney =false;
notify();
return i;
}
}
class Save implements Runnable
{
private Account a1;
public Save(Account a1)
{
this.a1 = a1;
}
public void run()
{
while(true){
a1.put(100);
}
}
}
class Fetch implements Runnable
{
private Account a1;
public Fetch(Account a1)
{this.a1 = a1 ;}
public void run()
{
while(true){
a1.get(100);
}
}
}
public class TestCommunicate{
public static void main(String[] args){
Account a1 = new Account();
new Thread(new Save(a1)).start();
new Thread(new Fetch(a1)).start();
}
}
//改编9.11
package com.runoob.test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
class Account {
volatile private int value;
volatile private boolean isMoney = false;
private final ReentrantLock lock = new ReentrantLock();
final Condition notput = lock.newCondition();//对应put
final Condition notget = lock.newCondition(); //对应get
void put(int i) throws InterruptedException {
lock.lock();
try {
if (isMoney) {
notput.await();
}
value = value + i;
System.out.println("存入" + i + " 账上金额为:" + value);
isMoney = true;
notget.signalAll();
} finally {
lock.unlock();
}
}
int get(int i) throws InterruptedException {
lock.lock();
try {
if (!isMoney) {
notget.await();
}
if (value > i)
value = value - i;
else {
i = value;
value = 0;
}
System.out.println("取走" + i + " 账上金额为:" + value);
isMoney = false;
notput.signalAll();
return i;
} finally {
lock.unlock();
}
}
}
class Save implements Runnable {
private Account a1;
public Save(Account a1) {
this.a1 = a1;
}
public void run() {
while (true) {
try {
a1.put(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class Fetch implements Runnable {
private Account a1;
public Fetch(Account a1) {
this.a1 = a1;
}
public void run() {
while (true) {
try {
a1.get(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class TestCommunicate {
public static void main(String[] args) {
Account a1 = new Account();
new Thread(new Save(a1)).start();
new Thread(new Fetch(a1)).start();
}
}
7. 编写一个多线程Java应用模拟生产者/消费者模型,各产生10个生产者和消费者线程,共享一个缓冲区队列(长度自设),生产者线程将产品放入到缓冲区,消费者线程从缓冲区取出产品。
package Testcom;
//仓库类
class Store_1 {
private final int MAX_SIZE;// 仓库的最大容量
private int count;// 当前的货物数量
public Store_1(int n) {// 初始化最大容量的构造方法
MAX_SIZE = n;
count = 0;
}
// 往仓库加货物的方法
public synchronized void add() {
while (count >= MAX_SIZE) {// 每次执行都判断仓库是否已满
System.out.println("已经满了");
try {
this.wait();// 如果满了,就进入等待池
} catch (Exception e) {
e.printStackTrace();
}
}
count++;// 数量加1
// 打印当前仓库的货物数量
System.out.println(Thread.currentThread().toString() + "put" + count);
// 仓库中已经有东西可以取了,则通知所有的消费者线程来拿
this.notifyAll();
}
// 从仓库拿走货物的方法
public synchronized void remove() {
while (count <= 0) {
System.out.println("空了");// 每次执行都判断仓库是否为空
try {
this.wait();// 如果为空,就进入等待池
} catch (Exception e) {
e.printStackTrace();
}
}
// 打印当前仓库的货物数量
System.out.println(Thread.currentThread().toString() + "get" + count);
count--;// 数量减1
// 仓库还没装满,通知生产者添加货物
this.notifyAll();
}
}
class Producer extends Thread {// 生产者线程类
private Store_1 s;
public Producer(Store_1 s) {
this.s = s;
}
public void run() {// 线程方法
while (true) {// 循环
s.add();// 往仓库加货物
try {
Thread.sleep(1000);// 设置线程休息1s
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Consumer extends Thread {// 消费者线程类
private Store_1 s;
public Consumer(Store_1 s) {
this.s = s;
}
public void run() {// 线程方法
while (true) {// 循环
s.remove();// 从仓库取走货物
try {
Thread.sleep(1500);// 设置线程休息1.5s
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class Store {
public static void main(String[] args) {
Store_1 s = new Store_1(5);
// 创建两个生产者和两个消费者
Thread pro1 = new Producer(s);
Thread con1 = new Consumer(s);
Thread pro2 = new Producer(s);
Thread con2 = new Consumer(s);
pro1.setName("producer1");
con1.setName("consumer1");
pro2.setName("producer2");
con2.setName("consumer2");
// 启动线程
pro1.start();
con1.start();
pro2.start();
con2.start();
}
}
/*
结果:
……
Thread[producer1,5,main]put5
Thread[consumer2,5,main]get5
Thread[consumer1,5,main]get4
Thread[producer2,5,main]put4
Thread[producer1,5,main]put5
已经满了
Thread[consumer2,5,main]get5
Thread[producer2,5,main]put5
Thread[consumer1,5,main]get5
Thread[producer1,5,main]put5
已经满了
Thread[consumer1,5,main]get5
......
*/