九、线程
1.FutureTask类有什么作用?它实现了哪些接口?Callable接口和Runnable接口有什么不同?
Future Task类的作用:
FutureTask是为了弥补Thread的不足而设计的,它可以让程序员准确地知道线程什么时候执行完成并获得到线程执行完成后返回的结果(如果有需要)。FutureTask是一种可以取消的异步的计算任务。它的计算是通过Callable实现的,它等价于可以携带结果的Runnable,并且有三个状态:等待、运行和完成。完成包括所有计算以任意的方式结束,包括正常结束、取消和异常。
实现的接口:
Future:封装并行调用的类,可以取消任务的执行,确定执行是否已成功完成或出错,以及其他操作。
Callable:用于实现并行执行的接口。它与 Runnable 接口非常相似,但是它不返回任何值,而 Callable 必须在执行结束时返回一个值。
Callable接口和Runnable接口:
相同点:
1.Callable和Runnable都是接口
2.Callable和Runnable都可以应用于Executors
不同点:
1.Callable要实现call方法,Runnable要实现run方法
2.call方法可以返回值,run方法不能
3.call方法可以抛出checked exception,run方法不能
2.请查阅JDK自学线程池的相关类,如ThreadPoolExecutor构造器各个参数的意义,利用线程池编写多线程程序。
参考博客:Java多线程和线程池详解_KongJHong的博客-CSDN博客
3.volatile关键字有什么作用?
Java总是从主存读取变量,但随着JVM的优化,线程可以把主存变量保存在寄存器(工作内存)中操作,线程结束再与主存变量进行同步,然而当线程没有执行结束就发生了互换这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器变量值的副本,造成数据的不一致。要解决这个问题就需要该变量声明为volatile(不稳定),它指示JVM这个变量是不稳定的,每次使用它都到主存中读取。
在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。volatile关键字能禁止指令重排序,能在一定程度上保证有序性。
//变量a为volatile型,其他变量不是
b=1; //语句1
c=2; //语句2
a=5; //语句3
d=6; //语句4
e=4; //语句5
能保证语句3在语句1和语句2之后,语句4和语句5之前执行,但不能保证语句1、2和语句4、5的执行顺序。
4.Java提供了哪些同步机制来实现互斥?
5.编写Java程序模拟烧水泡茶最优工序。
package homework.third;
public class MakeTea {
public static void main(String[] args) {
MakeTeaThread1 mk1=new MakeTeaThread1();
mk1.start();
}
}
package homework.third;
public class MakeTeaThread1 extends Thread{
private MakeTeaThread2 mk2;
public MakeTeaThread1() {
mk2=new MakeTeaThread2();
}
public void run() {
System.out.print(this.getName()+":洗水壶->");
try {
Thread.sleep(1000);
}
catch(Exception e) {}
System.out.println("烧水-------------------");
System.out.print(" |");
mk2.start();
try {
Thread.sleep(15000);
}
catch(Exception e) {}
System.out.println();
System.out.print(Thread.currentThread().getName()+": 泡茶");
}
}
package homework.third;
public class MakeTeaThread2 extends Thread{
public void run() {
System.out.println();
System.out.print(this.getName()+": 洗茶壶->");
try {
Thread.sleep(1000);
}
catch(Exception e) {}
System.out.print("洗茶杯->");
try {
Thread.sleep(2000);
}
catch(Exception e) {}
System.out.println("拿茶叶------");
System.out.print(" ↓");
}
}
结果:
6.请使用Java并发包的Lock及Conditon改写例9.11。
class Accounter{
volatile private int value;
//布尔标志
volatile private boolean isMoney = false;
//put设为同步方法
synchronized void put(int i) {
while(isMoney) {
try{
wait(); //等待并释放锁
} //线程等待
catch(Exception e){}
}
value = value + i;
System.out.println("存入"+i+" 账上金额为:"+value);
isMoney = true;//设置标志
notifyAll(); //唤醒等待资源的所有线程
}
synchronized int get(int i) {
while(!isMoney ){
try {
wait();
}
catch(Exception e){}
}
if (value>i)
value = value - i;
else {
i = value;
value = 0;
}
System.out.println("取走"+i+" 账上金额为:"+value);
isMoney = false;
notifyAll(); //并不释放锁
return i;
}
}
class Products{
public static void main(String[] args) {
Accounter a1=new Accounter();
//存钱线程
new Thread(() -> {
while(true){ a1.put(100);}
}).start();
//取钱线程
new Thread(() -> {
while(true){ a1.get(100); }
}).start();
}
}
import java.util.concurrent.locks.*;
class Accounter{
volatile private int value;
//布尔标志
volatile private boolean isMoney = false;
private final ReentrantLock lock=new ReentrantLock();
private Condition SaveCondition=lock.newCondition();
private Condition FetchCondition=lock.newCondition();
//put设为同步方法
void put(int i) {
lock.lock();
try {
while (isMoney) {
try {
SaveCondition.await();
} catch (Exception e) {}
}
value = value + i;
System.out.println("存入" + i + " 账上金额为:" + value);
isMoney = true;//设置标志
FetchCondition.signal();
}finally{
lock.unlock();
}
}
int get(int i) {
lock.lock();
try {
while (!isMoney) {
try {
FetchCondition.await();
} catch (Exception e) {
}
}
if (value > i)
value = value - i;
else {
i = value;
value = 0;
}
System.out.println("取走" + i + " 账上金额为:" + value);
isMoney = false;
SaveCondition.signal();
return i;
}finally{
lock.unlock();
}
}
}
class Products{
public static void main(String[] args) {
Accounter a1=new Accounter();
//存钱线程
new Thread(() -> {
while(true){ a1.put(100);}
}).start();
//取钱线程
new Thread(() -> {
while(true){ a1.get(100); }
}).start();
}
}
7.编写一个多线程Java应用模拟生产者消费者模型,各产生10个生产者和消费者线程,共享一个缓冲区队列 (长度自设),生产者线程将产品放入到缓冲区,消费者线程从缓冲区取出产品。
package homework.third;
public class Products {
volatile private int product=0;
private Integer flag;
Products(){
flag=new Integer(1);
}
public int getProduct() {
return product;
}
public void produceProduct(int a) {
synchronized(flag) {
if(product<100) {
if(product+a>100) {
int b=a;
a=100-product;
product=100;
System.out.println(Thread.currentThread().getName()+"produce "+b+", produce too many only accept"+a+"; Products:"+product);
}else {
product+=a;
System.out.println(Thread.currentThread().getName()+" produce "+a+"; Products:"+product);
}
}
}
}
public void consumeProduct(int a) {
synchronized(flag) {
if(product>0) {
if(product-a<0) {
int b=product;
product=0;
System.out.println(Thread.currentThread().getName()+"consume "+a+", consume too maney only supply"+b+"; Products:"+product);
}else {
product-=a;
System.out.println(Thread.currentThread().getName()+" consume "+a+"; Products:"+product);
}
}
}
}
public static void main(String[] args) {
Products pr=new Products();
Producer[] p;
p=new Producer[10];
for(int i=0;i<10;i++) {
p[i]=new Producer(pr);
}
Consumer[] c;
c=new Consumer[10];
for(int i=0;i<10;i++) {
c[i]=new Consumer(pr);
}
for(int i=0;i<10;i++) {
p[i].start();
}
for(int i=0;i<10;i++) {
c[i].start();
}
}
}
package homework.third;
import java.util.*;
public class Producer extends Thread{
private Products p;
private Random ra;
Producer(Products p){
this.p=p;
ra=new Random();
}
public void run() {
int a;
while(true) {
a=ra.nextInt(Integer.MAX_VALUE)%10;
p.produceProduct(a);
}
}
}
package homework.third;
import java.util.*;
public class Consumer extends Thread{
private Products p;
private Random ra;
Consumer(Products p){
this.p=p;
ra=new Random();
}
public void run() {
int a;
while(true) {
a=ra.nextInt(Integer.MAX_VALUE)%10;
p.consumeProduct(a);
}
}
}
生产者生产的最大数量为:100
一次运行的部分结果: