一、通过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阻塞队列方法;
关于wait()/notify()方法
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();
}
}
测试结果如下啊: