专栏首页编程坑太多Callable接口实现多线程,生产者消费者问题,多线下载(复制)文件

Callable接口实现多线程,生产者消费者问题,多线下载(复制)文件

一.通过Callable接口实现多线程

1.Callable接口介绍:

(1)java.util.concurrent.Callable是一个泛型接口,只有一个call()方法

(2)call()方法抛出异常Exception异常,且返回一个指定的泛型类对象

2.Callable接口实现多线程的应用场景

(1)当父线程想要获取子线程的运行结果时

3.使用Callable接口实现多线程的步骤

(1)第一步:创建Callable子类的实例化对象

(2)第二步:创建FutureTask对象,并将Callable对象传入FutureTask的构造方法中

(注意:FutureTask实现了Runnable接口和Future接口)

(3)第三步:实例化Thread对象,并在构造方法中传入FurureTask对象

(4)第四步:启动线程

例1(利用Callable接口实现线程):

利用Callable接口创建子线程类:

package call;
import java.util.concurrent.Callable;
/*
 * 实现Callable接口创建子线程,指明范型为返回的数据类型
 * */
public class CallDemo implements Callable<String> {
        @Override
        public String call() throws Exception {
            String th_name = Thread.currentThread().getName();
            System.out.println(th_name + "遭遇大规模敌军突袭...");
            System.out.println(th_name + "迅速变换阵型...");
            System.out.println(th_name + "极速攻杀敌军...");
            return "敌军损失惨重,我军大获全胜";
        }
}

实线程类:

package call;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class TestCallable {
    public static void main(String[] args) {
            CallDemo cl = new CallDemo();// 实例化Callable子类对象
            FutureTask<String> ft = new FutureTask<String>(cl);// 实例化FutureTask对象,并将Callable子类对象传入FutureTask的构造方法中
            new Thread(ft, "编程坑太多部队——>").start();// 启动线程
            Thread.currentThread().setName("李存勖部队——>");// 设置父线程名
    try {
            System.out.println(Thread.currentThread().getName() + "休整5000ms");
            Thread.sleep(5000);
    } catch (InterruptedException e) {
            e.printStackTrace();
    }
       System.out.println(Thread.currentThread().getName() + "休整完毕..");
    try {
            String str = ft.get();// 利用FutureTask对象调用get()方法获取子线程的返回值
            System.out.println(Thread.currentThread().getName() + "获取友军消息"
            + str);
    } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

李存勖部队——>休整5000ms

编程坑太多部队——>遭遇大规模敌军突袭...

编程坑太多部队——>迅速变换阵型...

编程坑太多部队——>极速攻杀敌军...

李存勖部队——>休整完毕..

李存勖部队——>获取友军消息敌军损失惨重,我军大获全胜

例2(匿名类部类实现Callable接口创建子线程):

匿名类部类实现Callable接口创建子线程类并实现:

package call;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//匿名类部类实现Callable接口创建子线程
public class AnonyCallable {
    public static void main(String[] args) {
    Callable<String> cl = new Callable<String>() {
    @Override
    public String call() throws Exception {
            System.out.println(Thread.currentThread().getName() + "正在行军~~~");
            System.out.println(Thread.currentThread().getName() + "遭遇敌军~~~");
            System.out.println(Thread.currentThread().getName() + "奋勇杀敌!!!!");
            return "战斗胜利,俘虏敌军50000人";
            }
};
        FutureTask<String> ft = new FutureTask(cl);
        new Thread(ft, "编程坑太多部队").start();
try {
        Thread.currentThread().setName("李存勖部队");
        Thread.sleep(3000);
        System.out.println(Thread.currentThread().getName() + "休整3000ms");
} catch (InterruptedException e) {
    e.printStackTrace();
}
    System.out.println(Thread.currentThread().getName() + "整顿完毕,等待友军消息...");
try {
            String str = ft.get();
            System.out.println("李存勖部队得知友军消息为:" + str);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
    }
}
}

运行结果:

    编程坑太多部队正在行军~~~

    编程坑太多部队遭遇敌军~~~

    编程坑太多部队奋勇杀敌!!!!

    李存勖部队休整3000ms

    李存勖部队整顿完毕,等待友军消息...

    李存勖部队得知友军消息为:战斗胜利,俘虏敌军50000人

二.生产者——消费者问题

生产者线程不断生产,消费者线程不断取走生产者生产的产品

Object中的几个方法支持:

(1)wait():线程等待,当前线程进入调用对象的线程——等待池

(2)Notify():唤醒一个等待线程

(3)notifyAll():唤醒全部的等到线程

注意:以上三个方法都必须在同步机制中调用

例3(生产者消费者问题(一对一)):

早餐基础类:

package one2one.producer;
// 早餐基础类
public class Breakfast {
   private String food;   // 吃的
   private String drink;  // 喝的
   private boolean flag=false;
   public synchronized void makeBreakfast(String food,String drink){
   if(flag){
   try {
   wait();   // 生产者线程进入同步对象维护的“线程等待池”
   } catch (InterruptedException e) {
   e.printStackTrace();
   }
   }
   this.food=food;
   try {
   Thread.sleep(1000);   // 休眠,但不释放“锁”
   } catch (InterruptedException e) {
   e.printStackTrace();
   }
   this.drink=drink;
   flag=true;
   notify();
   }
   public synchronized void eatBreakfast(){
   if(!flag){
   try {
   wait();   // 消费者线程进入同步对象维护的“线程等待池”,而且当前线程释放"锁"
   } catch (InterruptedException e) {
   e.printStackTrace();
   }
   }
   try {
   Thread.sleep(1000);
   } catch (InterruptedException e) {
   e.printStackTrace();
   }
   System.out.println(this.food+"=============>"+this.drink);
   flag=false;
   notify();
   }
}

生产者线程类:

package one2one.producer;
// 生产者线程
public class Producer implements Runnable{
    private Breakfast bf;
    public Producer(Breakfast bf){
     this.bf=bf;
    }
@Override
public void run() {
        for (int i = 1; i <=7; i++) {
if(i%2==0){
this.bf.makeBreakfast("bread","milk");
}else{
this.bf.makeBreakfast("馒头","稀饭");
}
}  
}
}

消费者线程类:

package one2one.producer;
// 消费者线程
public class Consumer implements Runnable{
    private Breakfast bf;
    public Consumer(Breakfast bf){
     this.bf=bf;
    }
@Override
public void run() {
for (int i = 1; i <=7; i++) {
System.out.println("星期"+i+"早餐种类:food======>drink");
this.bf.eatBreakfast();
}
}
}
测试类:
package one2one.producer;
public class Test {
public static void main(String[] args) {
Breakfast bf=new Breakfast();
new Thread(new Producer(bf)).start();   // 启动生产者线程
new Thread(new Consumer(bf)).start();   // 启动消费者线程
}
}

运行结果:

星期1早餐种类:food======>drink
馒头=============>稀饭
星期2早餐种类:food======>drink
bread=============>milk
星期3早餐种类:food======>drink
馒头=============>稀饭
星期4早餐种类:food======>drink
bread=============>milk
星期5早餐种类:food======>drink
馒头=============>稀饭
星期6早餐种类:food======>drink
bread=============>milk
星期7早餐种类:food======>drink
馒头=============>稀饭
(生产者消费者问题(many2many))

生产消费基础类:

package manytomany.product;
public class Product {
private int count = 0;// 商品数量
private int MAX = 10;// 最大库存
// 生产商品
public synchronized void makeProduct() {
String thread_name = Thread.currentThread().getName();// 获取生产者线程名
if (count > MAX) {
System.out.println("货物已满" + thread_name + "停止生产...");
try {
notifyAll(); // 唤醒所有的消费者线程
wait(); // 生产者线程停止生产
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++; // 生产者线程生产商品
System.out.println(thread_name + "生产了产品,目前商品总量:" + count);
notifyAll();// 唤醒所有消费者线程,模拟消费
}
}
// 消费商品
public synchronized void buyProduct() {
String thread_name = Thread.currentThread().getName();// 获取线程名
if (count <= 0) {
System.out.println("已无货," + thread_name + "停住消费...");
try {
notifyAll();// 唤醒生产者线程 生产商品
wait();// 消费者线程休眠,停止消费
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;// 开始 消费商品
System.out.println(thread_name + "消费了一个商品,目前商品数量为:" + count);
}
}
}

生产者线程:

package manytomany.product;
public class Producer implements Runnable {
private Product product; // 获取Product对象
public Producer(Product product) {
this.product = product;
}
@Override
public void run() {
while (true) {
product.makeProduct();// 调用生产方法
}
}
}
消费者线程:
package manytomany.product;
public class Consumer implements Runnable{
private Product product;
public Consumer(Product product) {
this.product = product;
}
@Override
public void run() {
while(true){
product.buyProduct();//调用消费方法
}
}
}
测试类:
package manytomany.product;
public class Test {
public static void main(String[] args) {
Product pro = new Product();
new Thread(new Producer(pro), "生产者1号——>").start();
new Thread(new Producer(pro), "生产者2号——>").start();
new Thread(new Producer(pro), "生产者3号——>").start();
new Thread(new Producer(pro), "生产者4号——>").start();
new Thread(new Consumer(pro), "消费者A——>").start();
new Thread(new Consumer(pro), "消费者B——>").start();
new Thread(new Consumer(pro), "消费者C——>").start();
new Thread(new Consumer(pro), "消费者D——>").start();
new Thread(new Consumer(pro), "消费者E——>").start();
}
}

运行结果(截取部分):

消费者E——>消费了一个商品,目前商品数量为:7

消费者E——>消费了一个商品,目前商品数量为:6

消费者E——>消费了一个商品,目前商品数量为:5

消费者A——>消费了一个商品,目前商品数量为:4

消费者A——>消费了一个商品,目前商品数量为:3

消费者A——>消费了一个商品,目前商品数量为:2

消费者A——>消费了一个商品,目前商品数量为:1

消费者A——>消费了一个商品,目前商品数量为:0

已无货,消费者A——>停住消费...

已无货,消费者C——>停住消费...

已无货,消费者D——>停住消费...

已无货,消费者B——>停住消费...

生产者1号——>生产了产品,目前商品总量:1

生产者1号——>生产了产品,目前商品总量:2

生产者1号——>生产了产品,目前商品总量:3

生产者1号——>生产了产品,目前商品总量:4

生产者1号——>生产了产品,目前商品总量:5

多线程下载(复制)文件

1.使用RandomAccessFile与InputStream的skip(long n)方法使每个线程负责文件的每一部分读写。

例(开启6个线程断点下载(复制)电影).

下载复制线程:

package download;
import java.io.*;
public class DownloadRunnable implements Runnable{
private File srcFile;   // 源文件路径
private long startPos;   // 每个线程的开始下载位置
private long partTask;   // 每个线程的下载任务
private RandomAccessFile raf;  // 用来写入
public DownloadRunnable(File srcFile,long startPos,long partTask,RandomAccessFile raf){
this.srcFile=srcFile;
this.startPos=startPos;
this.partTask=partTask;
this.raf=raf;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"准备从第"+startPos+"个字节开始读...");
InputStream input=null;
try {
input=new FileInputStream(srcFile);
input.skip(startPos);  // 跳过输入流的startPos个字节
byte[] b=new byte[1024*1024*10];
int len=0;
int count=0;   // 用来记录已经读写的字节数
while((len=input.read(b))!=-1 && count<partTask){
raf.write(b, 0, len);
count+=len;
}
System.out.println(Thread.currentThread().getName()+"已经写入了"+count+"个字节");
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
input.close();
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

开启线程类:

package dowload;
import java.io.*;
public class TestDown {
public static void main(String[] args) {
File srcFile = new File("e:" + File.separator + "哈利波特" + File.separator
+ "哈利波特与死亡圣器上.mkv");
long pathTask = srcFile.length() / 6;
for (int i = 0; i < 6; i++) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile("d:" + File.separator + "Movies.mkv", "rw");
long startPos = pathTask * i;
raf.seek(startPos);
new Thread(
new DownloadRunnable(srcFile, startPos, pathTask, raf),
"第" + (i + 1) + "条下载线程——>").start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

本文分享自微信公众号 - 编程坑太多(idig88),作者:看更多☞

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-03-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 『高级篇』docker之开发用户服务EdgeService(13)

    PS:其实通过梳理发现这个还是有套路可寻的如何多语言进行通信,先生成对应的语言的代码,然后通过rpc的服务端和客户端,他们之前进行协议话的通信,服务端完成自身的...

    IT故事会
  • Lambda表达式概述

    IT故事会
  • 「小程序JAVA实战」小程序首页视频(49)

    PS:主要说了关联查询的步骤,首选建立一个VO类,然后mapper关联VO类,增加对应xml文件返回VO类,service内添加分页插件,查询VO类,通过分页插...

    IT故事会
  • Java岗 面试考点精讲(基础篇01期)

    由于各种操作系统所支持的指令集不是完全一致,所以在操作系统之上加个虚拟机可以来提供统一接口,屏蔽系统之间的差异。

    阮键
  • Android如何获取QQ与微信的聊天记录并保存到数据库详解

    提前说明下:(该方法只适用于监控自己拥有的微信或者QQ ,无法监控或者盗取其他人的聊天记录。本文只写了如何获取聊天记录,服务器落地程序并不复杂,不做赘述。写的仓...

    砸漏
  • Android获取QQ和微信的聊天记录,并保存到数据库

    (该方法只适用于监控自己拥有的微信或者QQ ,无法监控或者盗取其他人的聊天记录。本文只写了如何获取聊天记录,服务器落地程序并不复杂,不做赘述。写的仓促,有错别字...

    秦穆之
  • 自定义FutureTask实现

    FutureTask是Future的实现类,用来异步任务的获取结果,可以启动和取消异步任务,查询异步任务是否计算结束以及获取最终的异步任务的结果。通过get()...

    CodingDiray
  • SpringMVC请求参数和响应结果全局加密和解密

    前段时间在做一个对外的网关项目,涉及到加密和解密模块,这里详细分析解决方案和适用的场景。为了模拟真实的交互场景,先定制一下整个交互流程。第三方传输(包括请求和响...

    Throwable
  • Android组件化专题 - 路由框架原理

    在路由框架之前,我们先了解什么是APT,并实践ButterKnife绑定findById的小功能。为什么先要讲解apt,因为路由的实现apt是核心的代码.看下面...

    用户3045442
  • Android Oss上传图片的使用示例

    1.在官网下载 sdk 包 2.解压后得到 jar 包,目前包括 aliyun-oss-sdk-android-x.x.x.jar、okhttp-3.x.x....

    砸漏

扫码关注云+社区

领取腾讯云代金券