一、通过Callable接口实现多线程
1、Callable接口介绍
java.util.concurrent.Callable是一个泛型接口,只有一个call()方法;
call()方法抛出Exception异常,且返回一个指定的泛型类的对象
2、Callable接口实现线程的应用场景
当父线程想获取子线程的运行结果时
3、使用Callable接口实现多线程的步骤
第一步:创建Callable子类的实例化对象;
第二步:创建FutureTask对象,并将Callable对象传入FutureTask的构造方法中(注意:FutureTask实现了Runnable接口和Future接口);
第三步:实例化Thread对象,并在构造方法中传入FutureTask对象;
第四步:启动线程。
二、生产者-消费者问题
生产者线程不断生产,消费者线程不断取走生产者生产的产品。
Object中的几个方法支持:
wait():线程等待,当前线程进入调用对象的线程等待池
notify():唤醒一个等待线程
notifyAll():唤醒全部等待的线程
注意:以上三个方法都必须在同步机制中调用
三、多线程下载(复制)文件
使用RandomAccessFile与InputStream的skip(long n)方法使每个线程负责文件的每一部分读写。
Callable实现多线程实例:
package callable;
import java.util.concurrent.Callable;
public class GeneralCallable implements Callable<String>{
@Override
public String call() throws Exception {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "开始偷袭...");
System.out.println(threadName + "与小股敌人作战");
return "偷袭成功!";
}
}
package callable;
import java.util.concurrent.FutureTask;
public class CallableDemo {
public static void main(String[] args) {
//创建Callable子类的实例化对象
GeneralCallable call = new GeneralCallable();
//创建FutureTask对象,并将Callable对象传入FutureTask构造方法中
FutureTask<String> task = new FutureTask<String>(call);
//实例化Thread对象,并在构造方法中传入FutureTask对象
Thread t = new Thread(task,"魏延线程");
t.start();//启动线程
System.out.println(Thread.currentThread().getName() + "进入休眠状态(休眠5秒钟)...");
try {
Thread.sleep(5000);
String report = task.get();//获取子线程的返回值
System.out.println("子线程返回的结果是:" + report);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
生产者-消费者(一对一)实例:
package OneToOne.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 OneToOne.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 OneToOne.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++) {
this.bf.eatBreakfast();
}
}
}
package OneToOne.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();
}
}
运行结果:
生产者-消费者(多对多)实例:
package ManyToMany;
public class Product {
private int count = 0;
private final int MAX = 5;//最大库存量
//生产商品
public synchronized void makeProduct() {
String threadName = Thread.currentThread().getName();
if(count >= MAX) {
System.out.println("货物已满" + threadName + "停止生产");
try {
notifyAll();//每生产一个商品,则通知(唤醒)所有等待着的消费者线程
wait();//生产者线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;//模拟生产
System.out.println(threadName + "生产了商品,目前商品总量:" + count);
notifyAll();
}
}
public synchronized void buyProduct() {
String threadName = Thread.currentThread().getName();
if(count <= 0) {
System.out.println(threadName + "已无货,请等待...");
notifyAll();//通知所有生产者线程
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;//模拟买商品
System.out.println(threadName + "买了一件商品,当前剩余商品数:" + count);
}
}
}
package ManyToMany;
public class Producer implements Runnable {
private Product product;
public Producer(Product product) {
this.product = product;
}
@Override
public void run() {
while(true) {
product.makeProduct();
}
}
}
package ManyToMany;
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;
public class Test {
public static void main(String[] args) {
Product pro = new Product();
new Thread(new Producer(pro),"1号生产者").start();//启动1号生产者
new Thread(new Producer(pro),"2号生产者").start();//启动2号生产者
new Thread(new Consumer(pro),"消费者A").start();//启动消费者A
new Thread(new Consumer(pro),"消费者B").start();//启动消费者B
}
}
运行结果:
多线程下载(复制)文件实例:
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) {
super();
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*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 download;
import java.io.*;
public class Client {
public static void main(String[] args) throws Exception {
File srcFile = new File("f:" + File.separator + "Future World Music - Dream Chasers.wav");
long partTask = srcFile.length() / 6; //准备使用6个线程,计算每个线程的下载任务
for(int i = 0;i < 6;i++) {
RandomAccessFile raf = new RandomAccessFile("d:" + File.separator + srcFile.getName(),"rw");
long startPos = i * partTask;//计算每条线程读写的起始位置
raf.seek(startPos);//设置每条线程的文件指针偏移量
new Thread(new DownloadRunnable(srcFile,startPos,partTask,raf)).start();
}
}
}
运行结果: