1.FutureTask类有什么作用?它实现了哪些接口?Callable接口和Runnable接口有什么不同?
FutureTask:一个可取消的异步计算。FutureTask提供了对Future的基本实现,可以调用方法去开始和取消一个计算,可以查询计算是否完成并且获取计算结果。只有当计算完成时才能获取到计算结果,一旦计算完成,计算将不能被重启或者被取消,除非调用runAndReset方法。
实现了java.util.concurrent.RunnableFuture 接口,该RunnableFuture 接口继承了java.util.concurrent.Future 接口与java.lang.Runnable接口。
Callable接口和Runnable接口的不同
>>Callable规定的方法是call(),而Runnable规定的方法是run().
>>Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
>>call() 方法可抛出异常,而run() 方法是不能抛出异常的。
>>运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。
>>它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
>>通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
>>Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
2.请查阅JDK自学线程池的相关类,如ThreadPoolExecutor构造器各个参数的意义, 利用线程池编写多线程程序。
3.volatile关键字有什么作用?
volatile(不稳定的),指示JVM这个变量是不稳定的,每次使用它需要到主存中进行读取。一般来说,多线程环境下各线程之间共享的变量都应该加上volatile
原因:线程可以把主存中的变量保存在寄存器中,线程结束时再与主存变量进行同步。然而,当线程没有结束时就发生互换可能会造成一个线程在主存中修改了一个变量的值,而另一个线程还在使用寄存器当中的副本
4.Java提供了哪些同步机制来实现互斥?
1. 对象监视器(monitor)与synchronized
Synchronized的三种用法:
synchronized 代码块:监视器就是指定的对象。
synchronized 方法:监视器就是this对象。
synchronized 静态方法:监视器就是相应的Class对象
2. Java并发包中的锁类:Lock接口,ReentrantLock类
5.编写Java程序模拟烧水泡茶最优工序。
class MakeTea { public static void main(String[] args) { MakeTeaThread1 mk1=new MakeTeaThread1(); mk1.start(); } } class MakeTeaThread1 extends Thread{ private MakeTeaThread2 mk2; public MakeTeaThread1() { mk2=new MakeTeaThread2(); } public void run() { System.out.print(this.getName()+":洗水壶->"); try { Thread.sleep(1000); } catch(Exception e) {} System.out.println("烧水-------------------"); System.out.print(" |"); mk2.start(); try { Thread.sleep(12000); } catch(Exception e) {} System.out.println(); System.out.print(Thread.currentThread().getName()+": 泡茶"); } } class MakeTeaThread2 extends Thread{ public void run() { System.out.println(); System.out.print(this.getName()+": 洗茶壶->"); try { Thread.sleep(1000); } catch(Exception e) {} System.out.print("洗茶杯->"); try { Thread.sleep(2000); } catch(Exception e) {} System.out.println("拿茶叶------"); System.out.print(" ↓"); } }
参考博客:(6条消息) volatile关键字有什么作用;编写Java程序模拟烧水泡茶最优工序;生产者和消费者线程。_weixin_43721225的博客-CSDN博客
6.请使用Java并发包的Lock及Conditon改写例9.11。
class Accounter{ volatile private int value; //布尔标志 volatile private boolean isMoney = false; //put设为同步方法 synchronized void put(int i) { while(isMoney) { try{ wait(); //等待并释放锁 } //线程等待 catch(Exception e){} } value = value + i; System.out.println("存入"+i+" 账上金额为:"+value); isMoney = true;//设置标志 notifyAll(); //唤醒等待资源的所有线程 } synchronized int get(int i) { while(!isMoney ){ try { wait(); } catch(Exception e){} } if (value>i) value = value - i; else { i = value; value = 0; } System.out.println("取走"+i+" 账上金额为:"+value); isMoney = false; notifyAll(); //并不释放锁 return i; } } class Products{ public static void main(String[] args) { Accounter a1=new Accounter(); //存钱线程 new Thread(() -> { while(true){ a1.put(100);} }).start(); //取钱线程 new Thread(() -> { while(true){ a1.get(100); } }).start(); } }
import java.util.concurrent.locks.*; class Accounter{ volatile private int value; //布尔标志 volatile private boolean isMoney = false; private final ReentrantLock lock=new ReentrantLock(); private Condition SaveCondition=lock.newCondition(); private Condition FetchCondition=lock.newCondition(); //put设为同步方法 void put(int i) { lock.lock(); try { while (isMoney) { try { SaveCondition.await(); } catch (Exception e) {} } value = value + i; System.out.println("存入" + i + " 账上金额为:" + value); isMoney = true;//设置标志 FetchCondition.signal(); }finally{ lock.unlock(); } } int get(int i) { lock.lock(); try { while (!isMoney) { try { FetchCondition.await(); } catch (Exception e) { } } if (value > i) value = value - i; else { i = value; value = 0; } System.out.println("取走" + i + " 账上金额为:" + value); isMoney = false; SaveCondition.signal(); return i; }finally{ lock.unlock(); } } } class Products{ public static void main(String[] args) { Accounter a1=new Accounter(); //存钱线程 new Thread(() -> { while(true){ a1.put(100);} }).start(); //取钱线程 new Thread(() -> { while(true){ a1.get(100); } }).start(); } }
7. 编写一个多线程Java应用模拟生产者/消费者模型,各产生10个生产者和消费者线程,共享一个缓冲区队列(长度自设),生产者线程将产品放入到缓冲区,消费者线程从缓冲区取出产品。
mport java.util.*; class Products { volatile private int product=0; public Integer flag ; Products(){ flag=new Integer(1); } public int getProduct() { return product; } public void produceProduct(int a) { synchronized(flag) { if(product<100) { if(product+a>100) { int b=a; a=100-product; product=100; System.out.println(Thread.currentThread().getName()+"produce "+b+", produce too many only accept"+a+"; Products:"+product); }else { product+=a; System.out.println(Thread.currentThread().getName()+" produce "+a+"; Products:"+product); } } } } public void consumeProduct(int a) { synchronized(flag) { if(product>0) { if(product-a<0) { int b=product; product=0; System.out.println(Thread.currentThread().getName()+"consume "+a+", consume too maney only supply"+b+"; Products:"+product); }else { product-=a; System.out.println(Thread.currentThread().getName()+" consume "+a+"; Products:"+product); } } } } public static void main(String[] args) { Products pr=new Products(); Producer[] p; p=new Producer[10]; for(int i=0;i<10;i++) { p[i]=new Producer(pr); } Consumer[] c; c=new Consumer[10]; for(int i=0;i<10;i++) { c[i]=new Consumer(pr); } for(int i=0;i<10;i++) { p[i].start(); } for(int i=0;i<10;i++) { c[i].start(); } } } class Producer extends Thread{ private Products p; private Random ra; Producer(Products p){ this.p=p; ra=new Random(); } public void run() { int a; while(true) { a=ra.nextInt(Integer.MAX_VALUE)%10; p.produceProduct(a); } } } class Consumer extends Thread{ private Products p; private Random ra; Consumer(Products p){ this.p=p; ra=new Random(); } public void run() { int a; while(true) { a=ra.nextInt(Integer.MAX_VALUE)%10; p.consumeProduct(a); } } }
生产者消费者问题是一个著名的线程同步问题,也是我们面试过程中,经常会问到的一个经典面试题,生产者负责生产数据放到缓冲区,消费者负责从缓存中里面取。
>>注意这个缓冲区的大小是有上限的。
>>生产者发现供过于求,也就是说生产的速度太快了,也就是说,缓冲区里的数据,远远高于消费者的消费的能力,如果我们不做处理,就会造成速度的堆积,所以,该缓冲区是有上限的。可以理解为一个仓库,只能存放一定数据的产品,仓库都堆放满了,已经无地可放产品了,只能等消费者购买产品后,我们才能继续生产产品。
>>生产者发现供不应求,说明消费者消费的速度远远高于生产者的速度,这时候,我们就只能耐心的等待。应该避免库里已经没有产品了还在消费。
class Maketea{ public static void main(String[] args) { Pile pile = new Pile(); for (int j = 0; j < 3; j++) { new Thread(()->{ for (int i = 0; i < 15; i++) { pile.pro(); } },"生产者"+j).start(); new Thread(()->{ for (int i = 0; i < 15; i++) { pile.consumer(); } },"消费者"+j).start(); } } } class Pile { //库存数量 private int sum=0; private int max=10; /** * 生产者 */ public synchronized void pro(){ while (sum == max){ //达到最大值说明库存已经满了,无法存放充电桩 try { this.wait(); //需要等待消费者,消费后,才能生存 } catch (InterruptedException e) { e.printStackTrace(); } } sum++; System.out.println(Thread.currentThread().getName()+":剩余充电桩数量为"+sum); this.notifyAll(); //通知消费者,我们库存有充电桩,赶紧来消费 } /** * 消费者 */ public synchronized void consumer(){ while (sum == 0){ try { this.wait(); //说明库房,充电桩不够了,需要等待充电桩厂家,继续生产充电桩 } catch (InterruptedException e) { e.printStackTrace(); } } sum --; System.out.println(Thread.currentThread().getName()+":剩余充电桩数量为"+sum); this.notifyAll(); //通知生产厂家,我已经消费了,你可以继续生产充电桩 } }
参考博客:(6条消息) 【多线程并发编程】十一 生产者和消费者问题(面试必问)_程序猿学社的博客-CSDN博客