Day39(生产者/消费者问题,实现Callable接口,线程池,FutureTask,ExecutorSevice,Executors)

Day39(生产者/消费者问题,实现Callable接口,线程池,FutureTask,ExecutorSevice,Executors)经典例题 生产者 消费者问题生产者 Productor 将产品交给店员 Clerk 而消费者 Customer 从店员处取走产品 店员一次只能持有固定数量的产品 比如 20 如果生产者试图生产更多的产品 店员会叫生产者停一下 如果店中有空位放产品了再通知生产者继续生产 如果店中没有产品了 店员会告诉消费者等一下 如果店中有产品了再通知消费者来取走产品

经典例题:生产者/消费者问题

  • 生产者比消费者快时,消费者会漏掉一些数据没有取到。
  • 消费者比生产者快时,消费者会取相同的数据。
/ * 经典例题:生产者/消费者问题 * 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品, * 店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店 * 员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产; * 如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。 * * 分析: * 1,是否是多线程问题? 是 : 生产线程 消费线程 * 2,是否有共享数据? 是 : 店员或产品 * 3,如何解决线程的安全问题? 同步机制,有三种方法 * 4,是否涉及线程的通信? 是 * */ class Cleck{ 
    private static int productCount = 0; //生产产品: public synchronized void produceProduct(){ 
    if (productCount < 20){ 
    productCount++; notify(); System.out.println(Thread.currentThread().getName()+ ":开始生产第"+productCount+"个产品"); }else{ 
    try { 
    wait(); } catch (InterruptedException e) { 
    e.printStackTrace(); } } } //消费产品: public synchronized void consumeProdcut(){ 
    if (productCount > 0){ 
    System.out.println(Thread.currentThread().getName()+ ":开始消费第"+productCount+"个产品"); productCount--; notify(); }else{ 
    try { 
    wait(); } catch (InterruptedException e) { 
    e.printStackTrace(); } } } } class Producer implements Runnable{ 
    private Cleck cle; public Producer(Cleck cle){ 
    this.cle = cle; } public void run(){ 
    while(true){ 
    try { 
    Thread.sleep(10); } catch (InterruptedException e) { 
    e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"开始生产~"); cle.produceProduct(); } } } class Consumer implements Runnable{ 
    private Cleck cle; public Consumer(Cleck cle){ 
    this.cle = cle; } public void run(){ 
    while(true){ 
    try { 
    Thread.sleep(30); } catch (InterruptedException e) { 
    e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"开始消费~"); cle.consumeProdcut(); } } } public class Product01 { 
    public static void main(String[] args) { 
    Cleck cle = new Cleck(); Producer pro = new Producer(cle); Consumer con = new Consumer(cle); Thread pro01 = new Thread(pro); pro01.setName("生产者一"); Thread con01 = new Thread(con); con01.setName("消费者一"); Thread con02 = new Thread(con); con02.setName("消费者二"); pro01.start(); con01.start(); con02.start(); } } 

自己写的:

class Productor extends Thread{ 
    static Object obj = new Object(); static int product = 0; int all = 0; public void run(){ 
    while(true){ 
    if (Customer01.product02+product == 50){ 
    break; } synchronized (obj) { 
    if (product >= 20){ 
    try { 
    obj.wait(); } catch (InterruptedException e) { 
    e.printStackTrace(); } } product++; all++; System.out.println("生产了"+product+"个"); System.out.println("一共生产了:"+all); } } } } class Customer01 extends Thread{ 
    static int product02 = 0; public void run(){ 
    while(true){ 
    synchronized (Productor.obj) { 
    if (Productor.product >0 && product02 < 50 ){ 
    Productor.product--; Productor.obj.notify(); product02++; System.out.println("客户买走了一个商品,客户共有"+product02); System.out.println("生产好的商品还剩于"+Productor.product); }else if(product02 == 50){ 
    break; } } } } } public class Product { 
    public static void main(String[] args) { 
    Productor pro = new Productor(); Customer01 cus = new Customer01(); pro.start(); cus.start(); } } 

JDK5.0新增线程创建方式一:实现Callable接口

与使用Runnable相比,Callable功能更强大些

  • 相比run()方法,可以有返回值
  • 方法可以抛出异常
  • 支持泛型的返回值
  • 需要借助FutureTask类,比如获取返回结果

Future接口

  • 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
  • FutrueTask是Futrue接口的唯一的实现类
  • FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; / * 创建线程的方式三,实现Callable接口。 ---- JDK 5.0新增 * 1,创建一个实现Callable接口的实现类 * 2,实现Call(),将线程需要执行的操作声明在Call()中;不需要返回值可以return null; * 3,创建Callable接口实现类的对象 * 4,将次Callable接口实现类的对象作为参数传递到FutureTask构造器中,并创建FutureTasd的对象 * 5,将FutureTask类的对象作为参数传递到Thread类的构造器中,并创建Thread的对象,并调用start(); * 6,获取Callable中call方法的返回值 * * 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式更强大? * 1,call()可以有返回值 * 2,call()可以抛出异常,被外面的操作捕获,获取异常信息 * 3,callable是支持泛型的 * */ //1,创建一个实现Callable接口的实现类 class NumThread implements Callable { 
    @Override //2,实现Call(),将线程需要执行的操作声明在Call()中;不需要返回值可以return null; public Object call() throws Exception { 
    int sum = 0; for (int i = 1 ; i <= 100 ; i++){ 
    if(i % 2 == 0){ 
    System.out.print(i + "\t"); sum += i; } } System.out.println(); return sum; } } public class CallableStudy { 
    public static void main(String[] args) { 
    //3,创建Callable接口实现类的对象 NumThread num01 = new NumThread(); //4,将次Callable接口实现类的对象作为参数传递到FutureTask构造器中,并创建FutureTasd的对象 FutureTask n01 = new FutureTask(num01); //5,将FutureTask类的对象作为参数传递到Thread类的构造器中,并创建Thread的对象,并调用start(); Thread n1 = new Thread(n01); n1.start(); try { 
    //6.get方法返回值即为FutureTask构造器参数Callable实现类重写的call()方法的返回值 Object abc = n01.get(); System.out.println("偶数的和为:"+abc); } catch (InterruptedException e) { 
    e.printStackTrace(); } catch (ExecutionException e) { 
    e.printStackTrace(); } } } 

JDK5.0新增线程创建方式二:使用线程池

背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

好处:

  • 提高响应速度(减少了创建新线程的时间)
  • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  • 便于线程管理
    • corePoolSize:核心池的大小
    • maximumPoolSize:最大线程数
    • keepAliveTime:线程没有任务时最多保持多长时间后会终止

线程池相关API

JDK 5.0起提供了线程池相关API:ExecutorService和Executors

ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

  • void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
  • Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
  • void shutdown() :关闭连接池

Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

  • Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
  • Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
  • Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
  • Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
/ * 创建线程的方式四: 使用线程池 * 1,提供指定数量的线程池 * 2,执行指定的线程的操作,需要提供实现Runnable接口或实现Callable接口实现类的对象 * 3,关闭连接池 * * 好处: * 1,提高响应速度(减少了创建新线程的时间) * 2,降低资源消耗(重复利用线程池中线程,不需要每次都创建) * 3,便于线程管理 * corePoolSize:核心池的大小 * maximumPoolSize:最大线程数 * keepAliveTime:线程没有任务时最多保持多长时间后会终止 */ class EvenNumbers implements Runnable{ 
    static Object obj = new Object(); @Override public void run() { 
    synchronized (obj) { 
    for(int i = 1 ; i <= 30 ; i++){ 
    obj.notify(); if (i % 2 == 0){ 
    System.out.println(Thread.currentThread().getName()+"输出了偶数:"+i); }try { 
    obj.wait(); } catch (InterruptedException e) { 
    e.printStackTrace(); } } } } } class CardinalNumbers implements Runnable{ 
    @Override public void run() { 
    synchronized (EvenNumbers.obj) { 
    for(int i = 1 ; i <= 30 ; i++){ 
    EvenNumbers.obj.notify(); if (i % 2 != 0){ 
    System.out.println(Thread.currentThread().getName()+"输出了奇数:"+i); } try { 
    EvenNumbers.obj.wait(); } catch (InterruptedException e) { 
    e.printStackTrace(); } } } } } public class ThreadPool { 
    public static void main(String[] args) { 
    //1,提供指定数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); //设置线程池的属性: ThreadPoolExecutor service01 = (ThreadPoolExecutor)service; service01.setCorePoolSize(5); System.out.println(service.getClass()); //class java.util.concurrent.ThreadPoolExecutor //2,执行指定的线程的操作,需要提供实现Runnable接口或实现Callable接口实现类的对象 service.execute(new EvenNumbers());//适合适用于Runnable service.execute(new CardinalNumbers()); //service.submit(new Evennumbers());//适合适用于Callable //3,关闭连接池 service.shutdown(); } } 
今天的文章 Day39(生产者/消费者问题,实现Callable接口,线程池,FutureTask,ExecutorSevice,Executors)分享到此就结束了,感谢您的阅读。
编程小号
上一篇 2024-12-13 10:17
下一篇 2024-12-13 10:11

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/85412.html