1.传统的线程通信
使用synchronized修饰的同步方法或者同步代码块,则要在方法体重直接调用wait()、notify()和notifyAll()3个方法。其中:
wait():使当前线程等待,知道其他线程调用该同步监视器的notify()或者notifyAll()方法。
notify():唤醒在此同步监视器上等待的单个线程。
notifyAll():唤醒在此同步监视器上等待的所有线程。
2.使用Condition控制线程通信
如果程序中不使用synchronized关键字实现同步的话,而是直接使用Lock对象保证同步,则不能使用上述3个方法。一般使用Lock对象的newCondition()方法返回的Condition实例来控制同步通信。即使用Condition类的await()、signal()、signalAll()方法分别代替上述的3个方法。
3.使用阻塞队列(BlockingQueue)控制线程通信
BlockingQueue是Queue的子接口,但当某个线程试图向BloockingQueue中放入元素时,如果队列已满,则该线程被阻塞,反之,若某个线程从队列中取元素且队列为空时,则该线程也会被阻塞。如下:
import java.util.concurrent.*;
class Producer extends Thread
{
private BlockingQueue<String> bq;
public Producer(BlockingQueue<String> bq)
{
this.bq=bq;
}
@Override
public void run()
{
String[] strArr=new String[]{"Java","讲义","Spring"};
for(int i=0;i<33333;i++)
{
System.out.println(getName()+"生产者生产");
try
{
Thread.sleep(200);
bq.put(strArr[i%3]);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(getName()+"生产完成:"+bq);
}
}
}
class Consumer extends Thread
{
private BlockingQueue<String> bq;
public Consumer(BlockingQueue<String> bq)
{
this.bq=bq;
}
@Override
public void run()
{
while(true)
{
System.out.println(getName()+"消费者准备消费!");
try
{
Thread.sleep(200);
bq.take();
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(getName()+"消费成功:"+bq);
}
}
}
public class BlockingQueueTest2
{
public static void main(String[] args)
{//程序中生产者和消费者依次执行
//创建只有1个元素的阻塞队列
BlockingQueue<String> bq=new ArrayBlockingQueue<>(1);
//启动3个生产者线程和1个消费者线程
new Producer(bq).start();
new Producer(bq).start();
new Producer(bq).start();
new Consumer(bq).start();
}
}
4.线程池
由于系统启动一个新线程的成本是比较高的,使用线程池可以在系统启动时就创建大量的空闲线程,并且线程池可以有效控制系统中并发执行的线程数,使系统保持高效的性能。使用方法如下:
import java.util.concurrent.*;
class MyThread implements Runnable
{//使用Runnable接口定义一个简单的线程类
public void run()
{
for(int i=0;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+"i的值为:"+i);
}
}
public static void main(String[] args)
{
//创建一个固定数量的线程池
ExecutorService pool=Executors.newFixedThreadPool(6);
//向线程池中添加2个线程
pool.submit(new MyThread());
pool.submit(new MyThread());
pool.shutdown();//关闭线程池
}
}
使用线程池计算多个分解的任务:
import java.util.concurrent.*;
import java.util.*;
class CalTask extends RecursiveTask<Integer>
{//使用多个线程实现累加和的功能
//定义最多累加20个数
private static final int THREADHOLD=20;
private int arr[];//累加元素
private int start;//开始位置
private int end;//结束位置
public CalTask(int[] arr,int start,int end)
{
this.arr=arr;
this.start=start;
this.end=end;
}
@Override
protected Integer compute()
{//线程执行函数
int sum=0;
if(end-start<THREADHOLD)
{
for(int i=start;i<end;i++)
{
sum=sum+arr[i];
}
return sum;
}
else
{
int mid=(start+end)/2;
CalTask left=new CalTask(arr,start,mid);
CalTask right=new CalTask(arr,mid,end);
//并行执行2个小任务
left.fork();
right.fork();
//将2个任务累加的结果合并
return left.join()+right.join();
}
}
public static void main(String[] args) throws Exception
{
int[] arr=new int[100];
Random rand=new Random();
int total=0;
for(int i=0;i<arr.length;i++)
{//初始化数组
arr[i]=rand.nextInt(20);
total+=arr[i];
}
System.out.println(total);
ForkJoinPool pool=new ForkJoinPool();
//提交可分解的任务
Future<Integer> future=pool.submit(new CalTask(arr,0,arr.length));
//获取最终的返回值
System.out.println(future.get());
pool.shutdown();
}
}