Java并发包-java.util.concurrent详解

转载自https://blog.csdn.net/axi295309066/article/details/65665090

一.阻塞队列BlockingQueue

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

BlockingQueue 具有 4 组不同的方法用于插入、移除以及对队列中的元素进行检查。如果请求的操作不能得到立即执行的话,每个方法的表现也不同。这些方法如下:

Java并发包-java.util.concurrent详解_第1张图片

  • 抛异常:如果试图的操作无法立即执行,抛一个异常。
  • 特定值:如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。
  • 阻塞:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
  • 超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是 true / false)。

无法向一个 BlockingQueue 中插入 null。如果你试图插入 null,BlockingQueue 将会抛出一个 NullPointerException。 
可以访问到 BlockingQueue 中的所有元素,而不仅仅是开始和结束的元素。比如说,你将一个对象放入队列之中以等待处理,但你的应用想要将其取消掉。那么你可以调用诸如 remove(o) 方法来将队列之中的特定对象进行移除。但是这么干效率并不高(译者注:基于队列的数据结构,获取除开始或结束位置的其他对象的效率不会太高),因此你尽量不要用这一类的方法,除非你确实不得不那么做。

BlockingQueue是一个接口,需要使用它来实现BlockingQueue,实现类包含:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、DelayQueue、PriorityQueue

ArrayBlockingQueue的用法和源代码分析:

package 阻塞队列;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueExample {
	public static void main(String[] args) throws Exception {  
		ArrayBlockingQueue queue = new ArrayBlockingQueue(1024);  
        Producer producer = new Producer(queue);  
        Consumer consumer = new Consumer(queue);  
        new Thread(producer).start();  
        new Thread(consumer).start();  
        Thread.sleep(4000);  
    }  
}
class Producer implements Runnable{  
    protected ArrayBlockingQueue queue = null;  
    public Producer(ArrayBlockingQueue queue) {  
        this.queue = queue;  
    }  
    public void run() {  
        try {  
            queue.put("1");  
            System.out.println("put"+1);
            Thread.sleep(1000);  
            queue.put("2");
            System.out.println("put"+2);
            Thread.sleep(1000);  
            queue.put("3"); 
            System.out.println("put"+3);
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}
class Consumer implements Runnable{  
    protected ArrayBlockingQueue queue = null;  
    public Consumer(ArrayBlockingQueue queue) {  
        this.queue = queue;  
    }  
    public void run() {  
        try {  
            System.out.println("take"+queue.take());  
            System.out.println("take"+queue.take());  
            System.out.println("take"+queue.take());  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}  

源代码:

    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

    private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

offer和poll也可以用,有返回值,不过会出现队列满了就丢弃元素的情况

DelayQueue实现了BlockingQueue接口,DelayQueue对元素持有直到一个特定延迟到期,注入其中的元素必须实现java.util.concurrent.Delayed接口,该接口定义:

public interface Delayed extends Comparable{

     public long getDelay(TimeUnit timeUnit);

}

DelayQueue 将会在每个元素的 getDelay() 方法返回的值的时间段之后才释放掉该元素。如果返回的是 0 或者负值,延迟将被认为过期,该元素将会在 DelayQueue 的下一次 take 被调用的时候被释放掉。

Delayed 接口也继承了 java.lang.Comparable 接口,这也就意味着 Delayed 对象之间可以进行对比。这个可能在对 DelayQueue 队列中的元素进行排序时有用,因此它们可以根据过期时间进行有序释放。

链阻塞队列LinkedBlockingQueue:没有定义上限将使用Integer.MAX_VALUE,也可以人为定义大小

优先级阻塞队列PriorityBlockingQueue:优先级队列的元素必须实现Comparable接口

同步队列SynchronousQueue:内部只能容纳单个元素,内部只能容纳一个元素,如果队列已有一元素的话,试图插入一个元素会阻塞,直到另一个线程将该元素从队列中抽走,注意与线程池结合,不放入队列,直接执行

阻塞双端队列BlockingDeque:使用举例如下

BlockingDeque deque = new LinkedBlockingQueue();

deque.addFirst("1");

deque.addLast("2");

String two = deque.takeLast();

String first = deque.takeFirst();

链阻塞双端队列如上

具体查看源代码

ArrayBlockingQueue queue = new ArrayBlockingQueue(2);  
LinkedBlockingQueue lbq;

 

并发导航映射ConcurrentNavigableMap:

java.ytil.comcurrentNavigableMap是一个支持并发访问的java.util.NavigableMap,它还能让它的子map具备并发访问的能力,所谓的‘子map’指的是诸如heapMap()、subMap()、tailMap()之类方法返回的map,子map仅仅是持有原Map的引用,所以会同时改变

headMap():headMap(T toKey)方法返回一个包含了小于给定的toKey的key的子map

ConcurrentNavigableMap map = new ConcurrentNavigableMap();

map.put("1","one");

map.put("2","two");

map.put("3","three");

ConcurrentNavigableMap headMap = map.headMap("2");

则headMap将指向一个含有键"1"的ConcurrentNavigableMap,因为只有一个键小于"2"

tailMap():headMap(T fromKey)方法返回一个包含了不小于给定的fromKey的key的子map

ConcurrentNavigableMap map = new ConcurrentNavigableMap();

map.put("1","one");

map.put("2","two");

map.put("3","three");

ConcurrentNavigableMap tailMap = map.tailMap("2");

subMap():subMap()方法返回原始map中,键介于from(包含)和to(不包含)之间的子map

 

闭锁 CountDownLatch

java.util.concurrent.CountDownLatch是一个并发构造,允许一个或者多个线程等待一系列指定操作的完成

CountDownLatch以一个给定的数量初始化,countDown()每被调用一次,这一数量就减一,通过调用await()方法,线程可以阻塞等待这一数量到达零,示例如下:

package 闭锁;

import java.util.concurrent.CountDownLatch;

public class TestCountDownLatch {
	public static void main(String[] args) {
		CountDownLatch latch = new CountDownLatch(3);
		Waiter waiter = new Waiter(latch);
		Waiter2 waiter2 = new Waiter2(latch);
		Decrementer decrementer = new Decrementer(latch);
		
		new Thread(waiter).start();
		new Thread(waiter2).start();
		new Thread(decrementer).start();
	}
}
class Waiter implements Runnable{
	CountDownLatch latch = null;
	public Waiter(CountDownLatch latch) {
		this.latch = latch;
	}
	@Override
	public void run() {
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("Waiter Released");
	}
}
class Waiter2 implements Runnable{
	CountDownLatch latch = null;
	public Waiter2(CountDownLatch latch) {
		this.latch = latch;
	}
	@Override
	public void run() {
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("Waiter2 Released");
	}
}
class Decrementer implements Runnable{
	CountDownLatch latch = null;
	public Decrementer(CountDownLatch latch) {
		this.latch = latch;
	}
	@Override
	public void run() {
		try {
			Thread.sleep(1000);
			this.latch.countDown();
			Thread.sleep(1000);
			this.latch.countDown();
			Thread.sleep(1000);
			this.latch.countDown();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		try {
			Thread.sleep(4000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("CountDown减为0");
	}
}

 

栅栏 CyclicBarrier :java.util.concurrent.CyclicBarrier类是一种同步机制能够对处理一些算法的线程实现同步,就是一个所有线程必须等待的栅栏,所有(一定数量的)线程等待栅栏CyclicBarrier达成,所有线程将释放掉继续运行

创建一个CyclicBarrier:在创建一个CyclicBarrier的时候需要定义有多少线程在被释放之前等待栅栏,实例

CyclicBarrier barrier = new CyclicBarrier(2);

等待一个CyclicBarrier:barrier.await(),当然也可以为等待线程设置一个超时时间,等待超过了超时时间之后,即时还没有达成N个线程等待CyclicBarrier的条件,该线程也会被释放出来,示例如下:

barrier.await(10, TimeUnit.SECONS);

满足一下条件都可以使等待的CyclicBarrier释放:

(1)最后一个线程到达CyclicBarrier(调用await())

(2)当前线程被其他线程打断(其他线程调用了这个线程的interrupt()方法)

(3)其他等待栅栏的线程因超时而被释放

(4)外部线程调用了栅栏的CyclicBarrier.reset()方法

CyclicBarrier行动:CyclicBarrier 支持一个栅栏行动,栅栏行动是一个 Runnable 实例,一旦最后等待栅栏的线程抵达,该实例将被执行,可以在 CyclicBarrier 的构造方法中将 Runnable 栅栏行动传给它

Runnable      barrierAction = ... ;  
CyclicBarrier barrier       = new CyclicBarrier(2, barrierAction); 

CyclicBarrier使用示例:

package 栅栏;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class TestCyclicBarrier {
	public static void main(String[] args) {
		Runnable barrierAction1 = new Runnable(){
			@Override
			public void run() {
				System.out.println("BarrierAction1 executed");
			}
		};
		Runnable barrierAction2 = new Runnable(){
			@Override
			public void run() {
				System.out.println("BarrierAction2 executed");
			}
		};
		CyclicBarrier barrier1 = new CyclicBarrier(2, barrierAction1);
		CyclicBarrier barrier2 = new CyclicBarrier(2, barrierAction2);
		CyclicBarrierRunnable barrierRunnable1 = 
				new CyclicBarrierRunnable(barrier1, barrier2);
		CyclicBarrierRunnable barrierRunnable2 = 
				new CyclicBarrierRunnable(barrier1, barrier2);
		new Thread(barrierRunnable1).start();
		new Thread(barrierRunnable2).start();
	}
}
class CyclicBarrierRunnable implements Runnable{
	CyclicBarrier barrier1 = null;
	CyclicBarrier barrier2 = null;

	public CyclicBarrierRunnable(CyclicBarrier barrier1,CyclicBarrier barrier2) {
		this.barrier1 = barrier1;
		this.barrier2 = barrier2;
		
	}
	@Override
	public void run() {
		try {
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName()+" waiting at barrier 1");
			this.barrier1.await();
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName()+" waiting at barrier 2");
			this.barrier2.await();
			System.out.println(Thread.currentThread().getName()+"===Done!");
		} catch (InterruptedException | BrokenBarrierException e) {
			e.printStackTrace();
		}
	}
	
}

CountDownLatch和CyclicBarrier的区别

 

交换机Exchanger:表示一种两个线程可以进行互相交换对象的汇合点

代码如下:

package 交换机;

import java.util.concurrent.Exchanger;

public class TestExchanger {
	public static void main(String[] args) {
		Exchanger exchanger = new Exchanger();
		ExchangerRunnable exchangerRunnable1 = 
				new ExchangerRunnable(exchanger, "A");
		ExchangerRunnable exchangerRunnable2 = 
				new ExchangerRunnable(exchanger, "B");
		new Thread(exchangerRunnable1).start();
		new Thread(exchangerRunnable2).start();
	}
}
class ExchangerRunnable implements Runnable{
	Exchanger exchanger = null;
	Object object = null;
	public ExchangerRunnable(Exchanger exchanger,Object object){
		this.exchanger = exchanger;
		this.object = object;
	}
	@Override
	public void run() {
		Object previous = this.object;
		try {
			this.object = this.exchanger.exchange(this.object);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+" exchanged "+previous+" for "+this.object);
	}
	
}

 

信号量Semaphore:java.util.concurrent.Semaphore是一个计数信号量,有两个主要方法:

acquire()与release()

计数信号量由一个指定数量的 “许可” 初始化。每调用一次 acquire(),一个许可会被调用线程取走。每调用一次 release(),一个许可会被返还给信号量。因此,在没有任何 release() 调用时,最多有 N 个线程能够通过 acquire() 方法,N 是该信号量初始化时的许可的指定数量,这些许可只是一个简单的计数器。

信号量主要有两种用途:

  • 保护一个重要(代码)部分防止一次超过 N 个线程进入
  • 在两个线程之间发送信号

还可以类同阻塞队列的多线程存取

信号量也可以设置true实现公平阻塞(类同Reentrantlock的公平锁)

 

执行器服务ExecutorService:java.util.concurrent.ExecutorService 接口表示一个异步执行机制,使我们能够在后台执行任务。因此一个 ExecutorService 很类似于一个线程池。实际上,存在于 java.util.concurrent 包里的 ExecutorService 实现就是一个线程池实现,示例如下:

ExecutorService executorService = Executors.newFixedThreadPool(10);

executeService.execute(new Runnable(){

         public void run(){

                   System.out.println("Asynchronous task");

         }

});

Java并发包-java.util.concurrent详解_第2张图片

ExecutorService 实现

ExecutorService 是个接口,它的实现类,java.util.concurrent 包提供了 ExecutorService 接口的以下实现类:

  • ThreadPoolExecutor
  • ScheduledThreadPoolExecutor

创建一个 ExecutorService

ExecutorService 的创建依赖于你使用的具体实现。但是你也可以使用 Executors 工厂类来创建 ExecutorService 实例。以下是几个创建 ExecutorService 实例的例子:

ExecutorService executorService1 = Executors.newSingleThreadExecutor();

ExecutorService executorService2 = Executors.newFixedThreadPool(10);

ExecutorService executorService3 = Executors.newScheduledThreadPool(10);

ExecutorService 使用

有几种不同的方式来将任务委托给 ExecutorService 去执行:

  • execute(Runnable)
  • submit(Runnable)
  • submit(Callable)
  • invokeAny(…)
  • invokeAll(…)

execute(Runnable)方法要求一个java.lang.Runnable对象,然后对它进行异步执行,示例如下:

ExecutorService executorService = Executors.newSingleThreadExecutor();

executorService.execute(new Runnable() {

public void run() { System.out.println("Asynchronous task"); }

});

executorService.shutdown();

没有办法得知被执行的 Runnable 的执行结果。如果有需要的可以使用 Callable,线程的创建中Callable也能获得返回值

submit(Runnable)也要求一个Runnable实现类,返回一个Future对象用来检查Runnable是否已经执行完毕

Future future = executorService.submit(new Runnable() { public void run() { System.out.println("Asynchronous task"); } }); future.get(); //returns null if the task has finished correctly.

submit(Callable)

submit(Callable) 方法类似于 submit(Runnable) 方法,除了它所要求的参数类型之外。Callable 实例除了它的 call() 方法能够返回一个结果之外和一个 Runnable 很相像。Runnable.run() 不能够返回一个结果。 
Callable 的结果可以通过 submit(Callable) 方法返回的 Future 对象进行获取。以下是一个 ExecutorService Callable 示例:

Future future = executorService.submit(new Callable()

{ public Object call() throws Exception { System.out.println("Asynchronous Callable"); return "Callable Result"; } });

System.out.println("future.get() = " + future.get());

输出

Asynchronous Callable

future.get() = Callable Result

T invokeAny(Collection> tasks)

invokeAny():该方法要求一系列的Callable或者其子接口的实例对象,调用这个方法并不会返回一个Future,但它返回其中一个Callable对象的结果,无法保证返回的是哪个Callable的结果,只能保证其中一个已执行完毕

ExecutorService executorService = Executors.newSingleThreadExecutor();

Set> callables = new HashSet>();

callables.add(new Callable() { public String call() throws Exception { return "Task 1"; } });

callables.add(new Callable() { public String call() throws Exception { return "Task 2"; } });

callables.add(new Callable() { public String call() throws Exception { return "Task 3"; } });

String result = executorService.invokeAny(callables);

System.out.println("result = " + result);

executorService.shutdown();

invokeAll()

invokeAll() 方法将调用你在集合中传给 ExecutorService 的所有 Callable 对象。invokeAll() 返回一系列的 Future 对象,通过它们你可以获取每个 Callable 的执行结果。

一个任务可能会由于一个异常而结束,因此它可能没有 “成功”。无法通过一个 Future 对象来告知我们是两种结束中的哪一种。

ExecutorService executorService = Executors.newSingleThreadExecutor();

Set> callables = new HashSet>();

callables.add(new Callable() { public String call() throws Exception { return "Task 1"; } });

callables.add(new Callable() { public String call() throws Exception { return "Task 2"; } });

callables.add(new Callable() { public String call() throws Exception { return "Task 3"; } });

List> futures = executorService.invokeAll(callables);

for(Future future : futures){ System.out.println("future.get = " + future.get()); }

executorService.shutdown();

 

ExecutorService关闭:

使用完ExecutorService之后应该将其关闭,以使其中的线程不在运行,例如main方法退出了应用,如果有活动的ExecutorService它将还会保持运行,ExecutorService里活动线程阻止了JVM的关闭

要终止ExecutorService里的线程需要调用ExecuteService的shutdown()方法,ExecutorService 并不会立即关闭,但它将不再接受新的任务,而且一旦所有线程都完成了当前任务的时候,ExecutorService 将会关闭。在 shutdown() 被调用之前所有提交给 ExecutorService 的任务都被执行。

如果想要想要立即关闭 ExecutorService,可以调用 shutdownNow() 方法。这样会立即尝试停止所有执行中的任务,并忽略掉那些已提交但尚未开始处理的任务。无法担保执行任务的正确执行。可能它们被停止了,也可能已经执行结束。

 

线程池执行者TheadPoolExecutor

java.util.concurrent.ThreadPoolExecutor 是 ExecutorService 接口的一个实现。ThreadPoolExecutor 使用其内部池中的线程执行给定任务(Callable 或者 Runnable)。

ThreadPoolExecutor 包含的线程池能够包含不同数量的线程。池中线程的数量由以下变量决定:

  • corePoolSize
  • maximumPoolSize

当一个任务委托给线程池时,如果池中线程数量低于 corePoolSize,一个新的线程将被创建,即使池中可能尚有空闲线程。 
如果内部任务队列已满,而且有至少 corePoolSize 正在运行,但是运行线程的数量低于 maximumPoolSize,一个新的线程将被创建去执行该任务。 

Java并发包-java.util.concurrent详解_第3张图片

该内容可参考https://blog.csdn.net/qq_27378875/article/details/81586174 

https://blog.csdn.net/qq_27378875/article/details/81604390

 

定时执行者服务 ScheduledExecutorService

java.util.concurrent.ScheduledExecutorService 是一个 ExecutorService, 它能够将任务延后执行,或者间隔固定时间多次执行。 任务由一个工作者线程异步执行,而不是由提交任务给 ScheduledExecutorService 的那个线程执行。

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(new Callable() { public Object call() throws Exception { System.out.println("Executed!"); return "Called!"; } }, 5, TimeUnit.SECONDS);

首先一个内置 5 个线程的 ScheduledExecutorService 被创建。之后一个 Callable 接口的匿名类示例被创建然后传递给 schedule() 方法。后边的俩参数定义了 Callable 将在 5 秒钟之后被执行。

ScheduledExecutorService 实现

ScheduledExecutorService 是一个接口,使用 java.util.concurrent 包里对它的某个实现类。ScheduledExecutorService 具有以下实现类:ScheduledThreadPoolExecutor

public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService

构造器类同ThreadPoolExecutor,调用super但是只有coreSize默认maximumSize为Integer.MAX_VALUE

一旦你创建了一个 ScheduledExecutorService,你可以通过调用它的以下方法:

  • schedule (Callable task, long delay, TimeUnit timeunit)
  • schedule (Runnable task, long delay, TimeUnit timeunit)
  • scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)
  • scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)

下面我们就简单看一下这些方法。

schedule (Callable task, long delay, TimeUnit timeunit)

这个方法计划指定的 Callable 在给定的延迟之后执行。 
这个方法返回一个 ScheduledFuture,通过可以在它被执行之前对它进行取消,或者在它执行之后获取结果。 
以下是一个示例:

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(new Callable() { public Object call() throws Exception { System.out.println("Executed!"); return "Called!"; } }, 5, TimeUnit.SECONDS);

System.out.println("result = " + scheduledFuture.get());

scheduledExecutorService.shutdown();

 

schedule (Runnable task, long delay, TimeUnit timeunit)

除了 Runnable 无法返回一个结果之外,这一方法工作起来就像以一个 Callable 作为一个参数的那个版本的方法一样,因此 ScheduledFuture.get() 在任务执行结束之后返回 null。

scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)

 

scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)

这一方法规划一个任务将被定期执行。该任务将会在首个 initialDelay 之后得到执行,然后每个 period 时间之后重复执行。

如果给定任务的执行抛出了异常,该任务将不再执行。如果没有任何异常的话,这个任务将会持续循环执行到 ScheduledExecutorService 被关闭。 
如果一个任务占用了比计划的时间间隔更长的时候,下一次执行将在当前执行结束执行才开始。计划任务在同一时间不会有多个线程同时执行。

scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)

除了 period 有不同的解释之外这个方法和 scheduleAtFixedRate() 非常像。

scheduleAtFixedRate() 方法中,period 被解释为前一个执行的开始和下一个执行的开始之间的间隔时间。

而在本方法中,period 则被解释为前一个执行的结束和下一个执行的结束之间的间隔。因此这个延迟是执行结束之间的间隔,而不是执行开始之间的间隔。

ScheduledExecutorService 关闭

正如 ExecutorService,在你使用结束之后你需要把 ScheduledExecutorService 关闭掉。否则他将导致 JVM 继续运行,即使所有其他线程已经全被关闭。

你可以使用从 ExecutorService 接口继承来的 shutdown() 或 shutdownNow() 方法将 ScheduledExecutorService 关闭。

 

使用ForkJoinPool进行分叉合并

ForkJoinPool 在 Java 7 中被引入。它和 ExecutorService 很相似,除了一点不同。ForkJoinPool 很方便地把任务分裂成几个更小的任务,这些分裂出来的任务也将会提交给 ForkJoinPool。任务可以继续分割成更小的子任务,只要它还能分割。

分叉

通过将自己分割成多个子任务,每个子任务可以由不同的CPU执行或者同一CPU的不同线程执行

只有当给定的任务过大,将它分割成子任务才有意义,将任务分割成子任务有一定开销,因此对于小型任务,这个分割的消耗可能比每个子任务并发执行的消耗还要大,将任务分割成子任务有意义的值被称为阈值

合并

当一个任务将自己分割成若干子任务后,该任务将进入等待所有子任务的结束之中

一旦子任务结束,该任务可以把所有结果合并为一个结果,并非所有类型的任务都会返回一个结果,如果这个任务不返回一个结果,它只能等待所有子任务执行完毕,也就不需要结果的合并

java.util.concurrent.ForkJoinPool

创建一个ForkJoinPool:ForkJoinPool forkJoinPool = new ForkJoinPool(4);

提交任务到ForkJoinPool:想提交任务到ExecutorService那样,将任务提交到ForkJoinPool,可以提交两种类型的任务,一种是没有任何返回值的一个"行动",一个是有返回值的一个"任务",两种类型分别由RecursiveAction和RecursiveTask表示

RecursiveAction是一种没有任何返回值的任务,它只是做一些工作,例如写数据到磁盘,然后退出,一个RecursiveAction可以将自己的工作分割成更小的几块,这样可以由独立的线程或者CPU执行

package 分支与合并;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class TestRecursiveAction {
	public static void main(String[] args) {
		//ForkJoinPool forkJoinPool = new ForkJoinPool(1); 
		//输出永远如下:
		/*
		Splitting workLoad : 24
		ForkJoinPool-1-worker-1  not Splitting and execute : 12
		ForkJoinPool-1-worker-1  not Splitting and execute : 12
		 */
		ForkJoinPool forkJoinPool = new ForkJoinPool(4);
		/*
		输出不定
		Splitting workLoad : 24
		ForkJoinPool-1-worker-1  not Splitting and execute : 12
		ForkJoinPool-1-worker-2  not Splitting and execute : 12
		=================或者
		Splitting workLoad : 24
		ForkJoinPool-1-worker-1  not Splitting and execute : 12
		ForkJoinPool-1-worker-1  not Splitting and execute : 12
		 */
		MyRecursiveAction myRecursiveAction = new MyRecursiveAction(24);
		forkJoinPool.invoke(myRecursiveAction);
	}
}
class MyRecursiveAction extends RecursiveAction{
	private long workLoad = 0;
	public MyRecursiveAction(long workLoad) {
		this.workLoad = workLoad;
	}
	@Override
	protected void compute() {
		if(this.workLoad>16){
			System.out.println("Splitting workLoad : " + this.workLoad);
			List subtasks = 
					new ArrayList<>();
			subtasks.addAll(createSubtasks());
			for(RecursiveAction subtask : subtasks){
				subtask.fork();
			}
		}
		else{
			System.out.println(Thread.currentThread().getName()+"  not Splitting and execute : " + this.workLoad);
		}
	}
	private List createSubtasks(){
		List subtasks = new ArrayList<>();
		MyRecursiveAction subtask1 = new MyRecursiveAction(this.workLoad/2);
		MyRecursiveAction subtask2 = new MyRecursiveAction(this.workLoad-this.workLoad/2);
		subtasks.add(subtask1);
		subtasks.add(subtask2);
		return subtasks;

	}
}

RecursiveTask是哟中可以返回结果的任务,可以将自己的任务分割为若干个更小任务,并将这些子任务的执行结果合并到有个集体结果,用法如下:

package 分支与合并;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.RecursiveTask;

public class TestRecursiveTask {
	public static void main(String[] args) {
		ForkJoinPool forkJoinPool = new ForkJoinPool(4);
		MyRecursiveTask myRecursiveTask = new MyRecursiveTask(128);
		System.out.println("result="+forkJoinPool.invoke(myRecursiveTask));
	}
}
class MyRecursiveTask extends RecursiveTask{
	private long workLoad = 0;
	public MyRecursiveTask(long workload){
		this.workLoad = workload;
	}
	@Override
	protected Long compute() {
		if(this.workLoad>16){
			System.out.println("Splitting workLoad : " + this.workLoad);
			List subtasks = 
					new ArrayList<>();
			subtasks.addAll(createSubtasks());
			for(MyRecursiveTask subtask : subtasks){
				subtask.fork();
			}
			long result = 0;
			for(MyRecursiveTask subtask : subtasks){
				result = result + subtask.join();
			}
			System.out.println("realresult="+result);
			return result;
		}
		else{
			System.out.println(Thread.currentThread().getName()+"  not Splitting and execute : " + this.workLoad);
			return workLoad*3;
		}
	}
	private List createSubtasks(){
		List subtasks = new ArrayList<>();
		MyRecursiveTask subtask1 = new MyRecursiveTask(this.workLoad/2);
		MyRecursiveTask subtask2 = new MyRecursiveTask(this.workLoad-this.workLoad/2);
		subtasks.add(subtask1);
		subtasks.add(subtask2);
		return subtasks;

	}
}

Lock 接口具有以下主要方法:

  • lock()

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

  • lockInterruptibly()

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

  • tryLock()

tryLock() 方法试图立即锁定 Lock 实例。如果锁定成功,它将返回 true,如果 Lock 实例已被锁定该方法返回 false。这一方法永不阻塞。

  • tryLock(long timeout, TimeUnit timeUnit)

tryLock(long timeout, TimeUnit timeUnit) 的工作类似于 tryLock() 方法,除了它在放弃锁定 Lock 之前等待一个给定的超时时间之外。

  • unlock()

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

java.util.concurrent.locks 包提供了 ReadWriteLock 接口的以下实现类:ReentrantReadWriteLock

ReadWriteLock 的创建以及如何使用它进行读、写锁定的简单示例代码,一个对读访问进行保护,一个对写访问进行保护:

package 读写锁;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

public class TestReadWriteLock {
	public static void main(String[] args) {
		ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();  

		ReadLock rl = readWriteLock.readLock(); 
		WriteLock wl = readWriteLock.writeLock();
		
		rl.lock();
		readWriteLock.readLock().unlock();  

		readWriteLock.writeLock().lock();  
 
		readWriteLock.writeLock().unlock();  
	}
}

 

原子类:

AtomicBoolean 

AtomicBoolean atomicBoolean = new AtomicBoolean();  //默认false

AtomicBoolean atomicBoolean = new AtomicBoolean(true);

boolean value = atomicBoolean.get();

atomicBoolean.set(false);

 getAndSet() 方法来交换一个 AtomicBoolean 实例的值。getAndSet() 方法将返回 AtomicBoolean 当前的值,并将为 AtomicBoolean 设置一个新值。示例如下:

AtomicBoolean atomicBoolean = new AtomicBoolean(true);

boolean oldValue = atomicBoolean.getAndSet(false);

compareAndSet() 方法允许你对 AtomicBoolean 的当前值与一个期望值进行比较,如果当前值等于期望值的话,将会对 AtomicBoolean 设定一个新值。compareAndSet() 方法是原子性的,因此在同一时间之内有单个线程执行它。因此 compareAndSet() 方法可被用于一些类似于锁的同步的简单实现

AtomicBoolean atomicBoolean = new AtomicBoolean(true);

boolean expectedValue = true;

boolean newValue = false;

boolean wasNewValueSet = atomicBoolean.compareAndSet(expectedValue, newValue);

 

AtomicInteger 

AtomicInteger atomicInteger = new AtomicInteger();  //默认为0

AtomicInteger atomicInteger = new AtomicInteger(123);

int theValue = atomicInteger.get();

AtomicInteger 类也通过了一个原子性的 compareAndSet() 方法。这一方法将 AtomicInteger 实例的当前值与期望值进行比较,如果二者相等,为 AtomicInteger 实例设置一个新值。AtomicInteger.compareAndSet() 代码示例:

AtomicInteger atomicInteger = new AtomicInteger(123);

int expectedValue = 123; int newValue = 234;

atomicInteger.compareAndSet(expectedValue, newValue);

AtomicInteger 类包含有一些方法,通过它们你可以增加 AtomicInteger 的值,并获取其值。这些方法如下:

  • addAndGet()
  • getAndAdd()
  • getAndIncrement()
  • incrementAndGet()

第一个 addAndGet() 方法给 AtomicInteger 增加了一个值,然后返回增加后的值。getAndAdd() 方法为 AtomicInteger 增加了一个值,但返回的是增加以前的 AtomicInteger 的值。具体使用哪一个取决于你的应用场景。以下是这两种方法的示例:

AtomicInteger atomicInteger = new AtomicInteger();

System.out.println(atomicInteger.getAndAdd(10));

System.out.println(atomicInteger.addAndGet(10));

本示例将打印出 0 和 20。例子中,第二行拿到的是加 10 之前的 AtomicInteger 的值。加 10 之前的值是 0。第三行将 AtomicInteger 的值再加 10,并返回加操作之后的值。该值现在是为 20。

你当然也可以使用这俩方法为 AtomicInteger 添加负值。结果实际是一个减法操作。

getAndIncrement() 和 incrementAndGet() 方法类似于 getAndAdd() 和 addAndGet(),但每次只将 AtomicInteger 的值加 1。

AtomicInteger 类还提供了一些减小 AtomicInteger 的值的原子性方法。这些方法是:

  • decrementAndGet()
  • getAndDecrement()

decrementAndGet() 将 AtomicInteger 的值减一,并返回减一后的值。getAndDecrement() 也将 AtomicInteger 的值减一,但它返回的是减一之前的值。

 

AtomicReference 提供了一个可以被原子性读和写的对象引用变量。原子性的意思是多个想要改变同一个 AtomicReference 的线程不会导致 AtomicReference 处于不一致的状态。AtomicReference 还有一个 compareAndSet() 方法,通过它你可以将当前引用于一个期望值(引用)进行比较,如果相等,在该 AtomicReference 对象内部设置一个新的引用。

AtomicReference atomicReference = new AtomicReference();

String initialReference = "the initially referenced string";

AtomicReference atomicReference = new AtomicReference(initialReference);

AtomicReference atomicStringReference = new AtomicReference();

String initialReference = "the initially referenced string";

AtomicReference atomicStringReference = new AtomicReference(initialReference);

可以通过 AtomicReference 的 get() 方法来获取保存在 AtomicReference 里的引用。如果你的 AtomicReference 是非泛型的,get() 方法将返回一个 Object 类型的引用。如果是泛型化的,get() 将返回你创建 AtomicReference 时声明的那个类型。

先来看一个非泛型的 AtomicReference get() 示例:

AtomicReference atomicReference = new AtomicReference("first value referenced");  
String reference = (String) atomicReference.get();  

泛型化的 AtomicReference 示例:

AtomicReference atomicReference =   new AtomicReference("first value referenced");
String reference = atomicReference.get();  

AtomicReference 类具备了一个很有用的方法:compareAndSet()。compareAndSet() 可以将保存在 AtomicReference 里的引用于一个期望引用进行比较,如果两个引用是一样的(并非 equals() 的相等,而是 == 的一样),将会给AtomicReference 实例设置一个新的引用。

如果 compareAndSet() 为 AtomicReference 设置了一个新的引用,compareAndSet() 将返回 true。否则compareAndSet() 返回 false。

AtomicReference compareAndSet() 示例:

String initialReference = "initial value referenced";  

AtomicReference atomicStringReference =  
    new AtomicReference(initialReference);  

String newReference = "new value referenced";  
boolean exchanged = atomicStringReference.compareAndSet(initialReference, newReference);  
System.out.println("exchanged: " + exchanged);  

exchanged = atomicStringReference.compareAndSet(initialReference, newReference);  
System.out.println("exchanged: " + exchanged);  
  • 本示例创建了一个带有一个初始引用的泛型化的 AtomicReference。之后两次调用 comparesAndSet()来对存储值和期望值进行对比,如果二者一致,为 AtomicReference 设置一个新的引用。第一次比较,存储的引用(initialReference)和期望的引用(initialReference)一致,所以一个新的引用(newReference)被设置给 AtomicReference,compareAndSet() 方法返回 true。第二次比较时,存储的引用(newReference)和期望的引用(initialReference)不一致,因此新的引用没有被设置给 AtomicReference,compareAndSet() 方法返回 false。

你可能感兴趣的:(Java知识点)