您的位置:首页 > 其它

20180126:通过Callable实现多线程、生产者-消费者问题、多线程下载(复制)文件

2018-01-27 22:16 525 查看
一、通过Callable实现多线程

 1.Callable接口介绍

java.util.concurrent.Callable是一个泛型类接口,其中只有一个call()方法,call()方法会抛出Exception异常,且返回一个指定的泛型类对象:public
V call();



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

 1. 创建Callable接口子类实例化对象.

 2. 创建FutureTask对象,FutureTask也是一个泛型类,且泛型要和Callable的泛型类型相同,

并将Callable对象传入FutureTask的构造方法中(FutureTask是Runnable接口的实现类)

 3. 实例化Thread对象,并在构造方法中传入FutureTask对象

 4. 启动线程.

 用Callable接口实现多线程的使用场景是:一个线程需要另一个线程返回一个值时用到这种方法启动线程

写一个用Callable接口实现多线程的Demo:

package callable;

import java.util.concurrent.Callable;

public class CallableDemo implements Callable<String>{

//实现Callable泛型类,重写call方法
public String call() throws Exception {
String str = Thread.currentThread().getName();
System.out.println(str+"开始偷袭长安。。。。");
System.out.println(str+"军路上遇到小股敌军势力。。。");
return "偷袭成功,不辱使命!";
}

}
 
package callable;

import java.util.concurrent.FutureTask;

public class RunCallable {

public static void main(String[] args) {

//创建Callable的子类实例化对象
CallableDemo cd = new CallableDemo();
//将Callable的子类实例化对象传递
FutureTask<String> ft = new FutureTask<>(cd);

try {
Thread.currentThread().setName("诸葛亮线程");
System.out.println(Thread.currentThread().getName()+"开始进军。。。");
Thread.sleep(2000);
//将ft对象作为参数传递到Thread对象中
Thread td = new Thread(ft, "魏延线程");
td.start();
//获取子线程的返回值
System.out.println("报告丞相:"+ft.get());
System.out.println(Thread.currentThread().getName()+"说:666");
} catch (Exception e) {
e.printStackTrace();
}

}

}
 测试结果为:



 

从上述方法中可以看出:用Callable接口实现的线程类启动仍然靠的是线程类的start()方法,但若想获得线程的返回值,就
FutureTask的对象调用get()方法获得线程的返回对象。

二、生产者-消费者问题

生产者-消费者问题是多线程的一个经典问题,它描述的是生产者线程和消费者线程公用一块缓冲区,生产者是向缓冲区中添加东西,

消费者线程可以从缓冲区取走产品;

解决生产者/消费者问题的方法可以分为两类:

1. 采用某种机制保护生产者和消费者之间的同步;

2. 在生产者和消费者之间建立一个管道。

第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。
在java中有四种方法支持同步,其中前三个方法是同步方法,一个管道方法,但管道方法不经常用,所以这里就先不讨论:
wait()/notify()、notifyAll()方法;(只讨论这个。。。)
await()/signal()方法;
BlockintQueue阻塞队列方法;
[b]关于wait()/notify()方法[/b]
wait()/notify()、notifyAll()方法是父类Object的两个方法:

wait(): 当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,是自己处于等待状态,让其他线程执行。
notify(): 当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程可发出可执行的通知,同时放弃锁,使自己处于等待状态。
 这就是生产者-消费者线程的基本模式,其实和日常生活中的买卖是一样的,生产者生产产品,由于市场作用生产的产品有上限,消费者不断购买产品,产品没了之后不能购买,但可以提醒生产者继续生产产品去让卖卖继续进行下去。

接下来写一个同步方法的生产者-消费者Demo:

package con2pro;

public class Producer {

private int count; //设置当前产品数量,初值定为0把
private final int MAX=5;

//写一个生产的方法
public synchronized void makeProduct(){

try {
String threadName = Thread.currentThread().getName(); //获得当前线程的线程名

if(count > MAX){
System.out.println(threadName+"生产的产品达到上限了。。。");
notifyAll();
wait();
}else{
System.out.println(threadName+"开始生产产品了,");
count++;
Thread.sleep(1000);
System.out.println(threadName+"生产了一件产品,现在产品的数量为:"+count);
notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}

}

//写一个消费产品的方法
public synchronized void buyProduct(){

try {
String threadName = Thread.currentThread().getName();

if(count <= 0){
System.out.println(threadName+"产品已售空,请等待生产者生产。。。");
notifyAll();
wait();
}else{
System.out.println(threadName+"开始购买产品了,");
count--;
System.out.println(threadName+"已经购买了产品,当前产品数为:"+count);

}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
写一个生产线程和购买线程:

package con2pro;

public class Product implements Runnable{

private Producer pro;

public Product(Producer pro){
this.pro = pro;
}

public void run(){
while(true){
pro.makeProduct();
}
}

}

package con2pro;

public class Consumer implements Runnable{

private Producer pro;

public Consumer(Producer pro){
this.pro = pro;
}

public void run(){
while(true){
pro.buyProduct();
}
}

}
写一个测试类:

package con2pro;

public class Run {

public static void main(String[] args) {

Producer pro = new Producer();

new Thread(new Product(pro),"卫龙").start();
new Thread(new Consumer(pro),"薇恩").start();
new Thread(new Consumer(pro),"德莱厄斯").start();

}

}


测试结果如下啊:



三、多线程下载(复制)文件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐