并发编程回顾:队列

原先多线程并发编程的学习笔记和代码整理一下贴上来。

---------------------------------

队列

可以使用同步队列来解决任务协作问题,同步队列在任意时刻都只允许一个任务插入或移除元素。

同步队列的实现:

1、java.util.concurrent包中的BlockingQueue接口提供了这个队列,且该接口有大量实现,举例如下:

首先定义一个任务Task类,该任务有3步操作:

class Task{
	public enum Status {FIRST,SECOND,THIRD};
	private Status status = Status.FIRST; //default
	private final int id;
	public Task(int id){
		this.id=id;
	}
	public void doFirst() throws Exception{
		System.out.println("taskId="+id+" doFirst!");
		TimeUnit.MILLISECONDS.sleep(1000);//执行第一步操作需要1s
		status=Status.FIRST;
	}
	public void doSecond() throws Exception{
		System.out.println("taskId="+id+" doSecond!");
		TimeUnit.MILLISECONDS.sleep(200);//执行第二步操作需要0.2s
		status=Status.SECOND;
	}
	public void doThird() throws Exception{
		System.out.println("taskId="+id+" doThird!");
		TimeUnit.MILLISECONDS.sleep(2000);//执行第三步操作需要2s
		status=Status.THIRD;
	}
	public Status getStatus(){
		return status;
	}
	public int getId(){
		return this.id;
	}
}

然后,对这个task进行处理,每一步都定义一个处理器,这里用到了LinkedBlockingQueue。

第一个任务处理器,启动task,并执行第一步操作doFirst,执行完后放入队列。

class HandlerFirst implements Runnable{
	private LinkedBlockingQueue<Task> firstQueue;
	private int count=0;//任务数
	public HandlerFirst(LinkedBlockingQueue<Task> firstQueue){
		this.firstQueue=firstQueue;
	}
	@Override
	public void run() {
		try{
			while(!Thread.interrupted()){
				Task task=new Task(count++);
				task.doFirst();
				firstQueue.put(task);
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

第二个处理器,从第一步完成的队列中取出任务,然后继续执行第二步操作,完成后再放入第二步完成队列:

class HandlerSecond implements Runnable{
	private LinkedBlockingQueue<Task> firstQueue,secondQueue;
	public HandlerSecond(LinkedBlockingQueue<Task> firstQueue,LinkedBlockingQueue<Task> secondQueue){
		this.firstQueue=firstQueue;
		this.secondQueue=secondQueue;
	}
	@Override
	public void run() {
		try{
			while(!Thread.interrupted()){
				Task task = firstQueue.take();
				task.doSecond();
				secondQueue.put(task);
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

第三步,从完成第二步的队列中取出任务继续处理,之后放入全部完成的队列:

class HandlerThird implements Runnable{
	private LinkedBlockingQueue<Task> secondQueue,finishQueue;
	public HandlerThird(LinkedBlockingQueue<Task> secondQueue,LinkedBlockingQueue<Task> finishQueue){
		this.secondQueue=secondQueue;
		this.finishQueue=finishQueue;
	}
	@Override
	public void run() {
		try{
			while(!Thread.interrupted()){
				Task task = secondQueue.take();
				task.doThird();
				finishQueue.put(task);
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

任务所有步骤都完成时的处理器,打印出taskid:

class FinishHandler implements Runnable{
	private LinkedBlockingQueue<Task> finishQueue;
	public FinishHandler(LinkedBlockingQueue<Task> finishQueue){
		this.finishQueue=finishQueue;
	}
	@Override
	public void run() {
		try{
			while(!Thread.interrupted()){
				Task task = finishQueue.take();
				System.out.println("******* finish! taskId="+task.getId());
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

最后,测试一下这个程序:

LinkedBlockingQueue<Task> firstQueue=new LinkedBlockingQueue<Task>(),
		          secondQueue=new LinkedBlockingQueue<Task>(),
		          finishQueue=new LinkedBlockingQueue<Task>();
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new HandlerFirst(firstQueue));
es.execute(new HandlerSecond(firstQueue,secondQueue));
es.execute(new HandlerThird(secondQueue,finishQueue));
es.execute(new FinishHandler(finishQueue));
es.shutdown();

定义3个阻塞队列,分别存放第一步、第二步、第三步完成后任务的队列。

然后分别启动他们,任务会不断的创建并将各个步骤完成的结果放入不同的队列中。

由于BlockingQueue内部已经进行了同步处理,所以并发访问时不再需要同步代码。

2、管道

主要就是使用管道流实现该功能:

首相,定义一个Sender,该sender不停的把字符写入流中:

class Sender implements Runnable {
  private Random rand = new Random();
  private PipedWriter out = new PipedWriter();
  public PipedWriter getPipedWriter() { return out; }
  public void run() {
    while(true) {
      for(char c = 'A'; c <= 'z'; c++) {
        try {
          out.write(c);
          TimeUnit.MILLISECONDS.sleep(rand.nextInt(2000));
        } catch(Exception e) {
          throw new RuntimeException(e);
        }
      }
    }
  }
}
然后定义一个Receiver,不断的从管道中读取数据:
class Receiver implements Runnable {
  private PipedReader in;
  public Receiver(Sender sender) throws IOException {
    in = new PipedReader(sender.getPipedWriter());
  }
  public void run() {
    try {
      while(true) {
        // Blocks until characters are there:
        System.out.println("Read: " + (char)in.read());
      }
    } catch(IOException e) {
      throw new RuntimeException(e);
    }
  }
}
最后,测试一下这个程序,首先启动sender,然后启动receiver:
Sender sender = new Sender();
Receiver receiver = new Receiver(sender);
ExecutorService es = Executors.newCachedThreadPool();
es.execute(sender);
es.execute(receiver);

sender会不停的向管道中写入,receiver不停的从管道中读取,实现了一个队列的功能。

你可能感兴趣的:(并发编程)