Java并发包 java.util.concurrent解析

1. 概述

        java.util.concurrent 包是JDK1.5之后加入的。这个包包含有一系列能够让 Java 的并发编程变得更加简单轻松的类。在这个包被添加以前,你需要自己去动手实现自己的相关工具类。这里我们基于jdk api来简单熟悉下concurrent 包下面的而一些比较常用的接口和类。

2. 接口

1. public interface ThreadFactory

根据需要创建新线程的对象

例子

package com.curr.demo;

import java.util.concurrent.ThreadFactory;

public class ThreadFactoryDemo implements ThreadFactory {

	public Thread newThread(Runnable run) {
		return new Thread(run);
	}

	public static void main(String[] args) {
		ThreadFactory th = new ThreadFactoryDemo();
		th.newThread(new Runnable() {
			public void run() {
				System.out.println("thread run");
			}
		}).start();

	}

}

运行结果:

thread run

2. public interface BlockingQueue<E> extends Queue<E> ;

BlockingQueue 通常用于一个线程生产对象,而另外一个线程消费这些对象的场景.

有四组不同的方法分别进行插入,移除,检查。

方法 抛异常 特定值 阻塞 超时
插入 add(o) offer(o) put(o)  offer(o, time, unit)
移除 remove(o) poll(o) take(o)   poll(time, unit)
检查 element(o)    peek(o)    

这个接口有下面几个实现

下面是我写的一个爬虫中的应用

public class QuestionQueue {
	public static BlockingQueue<Question> bq = new LinkedBlockingQueue<Question>();
	public static int size = 0;
	static DataSource ds = DSFactory.get();
	static SqlRunner sqlrunner = SqlRunner.create(ds);

	/**
	 * 将用户信息放入队列中
	 * 
	 * @param u
	 */
	public synchronized static void putQuestionToQueue(Question q) {
		try {
			bq.put(q);
		} catch (InterruptedException e) {
			System.out.println("放入队列出错");
			e.printStackTrace();
		}
	}

	/**
	 * 获取当前队列长度
	 */
	public synchronized static void setQueueSize() {
		size = bq.size();
		System.out.println("size:" + size);
	}

	/**
	 * 将用户信息持久化到数据库
	 */
	public static void persistUser() {
		if (size > 100) {
			for (int i = 0; i < size; i++) {
				try {
					sqlrunner.insert(CollectionUtil.newArrayList(Entity.parse(bq.poll())));
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			System.out.println("持久化[" + size + "]条用户信息成功");
		}

	}
        在这个例子中,我将爬虫爬取的信息不直接存入数据库,而是通过异步方式先放入队列,并使用定时任务检测队列大小,达到一定程度才进行持久化,


对队列进行操作常用方法使用情况

boolean add(E e)

将指定元素插入此队列中(如果立即可行且不会违反容量限制),成功时返回 true,如果当前没有可用的空间,则抛出 IllegalStateException。当使用有容量限制的队列时,通常首选 offer

boolean offer(E e)

将指定元素插入此队列中(如果立即可行且不会违反容量限制),成功时返回 true,如果当前没有可用的空间,则返回 false。当使用有容量限制的队列时,此方法通常要优于 add(E),后者可能无法插入元素,而只是抛出一个异常。

void put(E e)

将指定元素插入此队列中,将等待可用的空间(如果有必要)

E take()
获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。
E poll(long timeout,
       TimeUnit unit)

获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。

3. Callable<V>

返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。

Callable 接口类似于 Runnable。 Runnable 执行任务时候不能有返回值,并且无法抛出经过检查的异常,但是Callable可以有返回值。

import java.util.concurrent.*;

	public class Demo {

	    public static void main(String[] args) throws ExecutionException, InterruptedException {
	        ExecutorService executor = Executors.newCachedThreadPool();
	        Future<String> future = executor.submit(new Callable<String>() {
	            @Override
	            public String call() throws Exception {
	                System.out.println("call");
	                TimeUnit.SECONDS.sleep(1);
	                return "str";
	            }
	        });
	        System.out.println(future.get());
	    }
	}


4.ExecutorService

超级接口:Executor

子接口:AbstractExecutorServiceScheduledThreadPoolExecutorThreadPoolExecutor

java.util.concurrent.ExecutorService 接口表示一个异步执行机制,其实我们也可以理解为是一个线程池。

下面就是一个简单的例子

ExecutorService executorService = Executors.newFixedThreadPool(10);  
 
executorService.execute(new Runnable() {  
    public void run() {  
        System.out.println("Asynchronous task");  
    }  
});  
 
executorService.shutdown();

值得注意的是,ExecutorService里的线程只有执行完任务,才可以终止,加入调用了 shutdown()方法,ExecutorService 只是会停止在接受新的任务,直到线程执行完后JVM才会销毁线程。

使用ExecutorService的时候,你可以使用Executors 工厂类来生成不同类型的线程池。

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 

5. java.util.concurrent.locks.Lock 

public interface Lock;

是一个类似于synchronized 的在java中用于同步的一个接口。

Lock实现了下面几个方法
package java.util.concurrent.locks;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;

public interface Lock {
	void lock();

	void lockInterruptibly() throws InterruptedException;

	boolean tryLock();

	boolean tryLock(long arg0, TimeUnit arg2) throws InterruptedException;

	void unlock();

	Condition newCondition();
}

lock() 将 Lock 实例锁定。如果该 Lock 实例已被锁定,调用 lock() 方法的线程将会阻塞,直到 Lock 实例解锁。

lockInterruptibly() 方法将会被调用线程锁定,除非该线程被打断。此外,如果一个线程在通过这个方法来锁定 Lock 对象时进入阻塞等待,而它被打断了的话,该线程将会退出这个方法调用。

tryLock() 方法试图立即锁定 Lock 实例。如果锁定成功,它将返回 true,如果 Lock 实例已被锁定该方法返回 false。这一方法永不阻塞。tryLock(long timeout, TimeUnit timeUnit) 的工作类似于 tryLock() 方法,除了它在放弃锁定 Lock 之前等待一个给定的超时时间之外。

unlock() 方法对 Lock 实例解锁。一个 Lock 实现将只允许锁定了该对象的线程来调用此方法。其他(没有锁定该 Lock 对象的线程)线程对 unlock() 方法的调用将会抛一个未检查异常(RuntimeException)


。。。今天先写这些吧,后续再补充













猜你喜欢

转载自blog.csdn.net/csgarten/article/details/80782208