精华内容
下载资源
问答
  • Java并发例子

    2019-11-12 16:43:09
    import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executor; import java.util.concurrent.Executors; public class Main { pr...
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    public class Main {
        private final static Executor executor = Executors.newCachedThreadPool();//启用多线程
        final static  CyclicBarrier barrier = new CyclicBarrier(4);
        public static void main(String[] args) {
             int data[] = new int[4];
            for(int i=0;i<=3; i++){
                final int j=i;
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        data[j]=j;
    //                    System.out.println(j+"----" + Thread.currentThread().getName());
    //                   System.out.println(data[j]);
                        try{
                            barrier.await();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
    
                    }
                });
            }
    
    //        try {
    //            Thread.sleep(30000); //1000 毫秒,也就是1秒.
    //        } catch(InterruptedException ex) {
    //            Thread.currentThread().interrupt();
    //        }
            try {
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            for (int k=0;k<=3;k++){
                System.out.println(data[k]);
            }
        }
    }
    

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

    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    public class Main {
        private final static Executor executor = Executors.newCachedThreadPool();//启用多线程
        final static  CyclicBarrier barrier = new CyclicBarrier(4);
        public static void main(String[] args) {
             //int data[] = new int[4];
            String[] s= new String[4];
            for(int i=0;i<=3; i++){
                final int j=i;
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        s[j]="aaaaaaaaa"+j;
    //                    System.out.println(j+"----" + Thread.currentThread().getName());
    //                   System.out.println(data[j]);
                        try{
                            barrier.await();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
    
                    }
                });
            }
    
    //        try {
    //            Thread.sleep(30000); //1000 毫秒,也就是1秒.
    //        } catch(InterruptedException ex) {
    //            Thread.currentThread().interrupt();
    //        }
            try {
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            for (int k=0;k<=3;k++){
                System.out.println(s[k]);
            }
        }
    }
    
    
    import java.util.concurrent.*;
    
    public class Main {
        public static void main(String[] args) throws InterruptedException {
            ExecutorService exector = Executors.newFixedThreadPool(12);
            int threadNumber = 10;
            String[] s = new String[10];
            final CountDownLatch countDownLatch = new CountDownLatch(threadNumber);
            for (int i = 0; i < threadNumber; i++) {
                final int threadID = i;
                exector.execute(
                        () -> {
    
                            try {
                                Thread.sleep(3000);
                                s[threadID] = "aaaaaaaaa" + threadID;
                                System.out.println(String.format("threadID:[%s] finished!!", threadID));
                            } catch (Exception e) {
                                e.printStackTrace();
                            } finally {
                                countDownLatch.countDown();  //这个不管是否异常都需要数量减,否则会被堵塞无法结束
                            }
    
    
                        }
                );
            }
            countDownLatch.await();//保证之前的所有的线程都执行完成,才会走下面的
            System.out.println(countDownLatch.getCount());
            System.out.println("main thread finished!!");
    
            for (int k = 0; k < s.length; k++) {
                System.out.println(s[k]);
            }
    
    
        }   
    
    
    }
    
    
     
    展开全文
  • 最近把《java并发编程实战》-Java Consurrency in Practice 重温了一遍,把书中提到的一些常用工具记录于此:一、闭锁(门栓)-CountDownLatch适用场景:多线程测试时,通常为了精确计时,要求所有线程都ready后,才...

    最近把《java并发编程实战》-Java Consurrency in Practice 重温了一遍,把书中提到的一些常用工具记录于此:

    一、闭锁(门栓)- CountDownLatch

    适用场景:多线程测试时,通常为了精确计时,要求所有线程都ready后,才开始执行,防止有线程先起跑,造成不公平,类似的,所有线程执行完,整个程序才算运行完成。

    /**

    * 闭锁测试(菩提树下的杨过 http://yjmyzz.cnblogs.com/)

    *

    * @throws InterruptedException

    */

    @Test

    public void countdownLatch() throws InterruptedException {

    CountDownLatch startLatch = new CountDownLatch(1); //类似发令枪

    CountDownLatch endLatch = new CountDownLatch(10);//这里的数量,要与线程数相同

    for (int i = 0; i < 10; i++) {

    Thread t = new Thread(() -> {

    try {

    startLatch.await(); //先等着,直到发令枪响,防止有线程先run

    System.out.println(Thread.currentThread().getName() + " is running...");

    Thread.sleep(10);

    } catch (InterruptedException e) {

    Thread.currentThread().interrupt();

    } finally {

    endLatch.countDown(); //每个线程执行完成后,计数

    }

    });

    t.setName("线程-" + i);

    t.start();

    }

    long start = System.currentTimeMillis();

    startLatch.countDown();//发令枪响,所有线程『开跑』

    endLatch.await();//等所有线程都完成

    long end = System.currentTimeMillis();

    System.out.println("done! exec time => " + (end - start) + " ms");

    }

    执行结果:

    线程-1 is running...

    线程-5 is running...

    线程-8 is running...

    线程-4 is running...

    线程-3 is running...

    线程-0 is running...

    线程-2 is running...

    线程-9 is running...

    线程-7 is running...

    线程-6 is running...

    done! exec time => 13 ms

    注:大家可以把第14行注释掉,再看看运行结果有什么不同。

    二、信号量(Semaphore)

    适用场景:用于资源数有限制的并发访问场景。

    public class BoundedHashSet {

    private final Set set;

    private final Semaphore semaphore;

    public BoundedHashSet(int bound) {

    this.set = Collections.synchronizedSet(new HashSet());

    this.semaphore = new Semaphore(bound);

    }

    public boolean add(T t) throws InterruptedException {

    if (!semaphore.tryAcquire(5, TimeUnit.SECONDS)) {

    return false;

    }

    ;

    boolean added = false;

    try {

    added = set.add(t);

    return added;

    } finally {

    if (!added) {

    semaphore.release();

    }

    }

    }

    public boolean remove(Object o) {

    boolean removed = set.remove(o);

    if (removed) {

    semaphore.release();

    }

    return removed;

    }

    }

    @Test

    public void semaphoreTest() throws InterruptedException {

    BoundedHashSet set = new BoundedHashSet<>(5);

    for (int i = 0; i < 6; i++) {

    if (set.add(i + "")) {

    System.out.println(i + " added !");

    } else {

    System.out.println(i + " not add to Set!");

    }

    }

    }

    上面的示例将一个普通的Set变成了有界容器。执行结果如下:

    0 added !

    1 added !

    2 added !

    3 added !

    4 added !

    5 not add to Set!

    三、栅栏CyclicBarrier

    这个跟闭锁类似,可以通过代码设置一个『屏障』点,其它线程到达该点后才能继续,常用于约束其它线程都到达某一状态后,才允许做后面的事情。

    public class Worker extends Thread {

    private CyclicBarrier cyclicBarrier;

    public Worker(CyclicBarrier cyclicBarrier) {

    this.cyclicBarrier = cyclicBarrier;

    }

    private void step1() {

    System.out.println(this.getName() + " step 1 ...");

    }

    private void step2() {

    System.out.println(this.getName() + " step 2 ...");

    }

    public void run() {

    step1();

    try {

    cyclicBarrier.await();

    } catch (InterruptedException e) {

    e.printStackTrace();

    } catch (BrokenBarrierException e) {

    e.printStackTrace();

    }

    step2();

    }

    }

    @Test

    public void cyclicBarrierTest() throws InterruptedException, BrokenBarrierException {

    CyclicBarrier cyclicBarrier = new CyclicBarrier(11);

    for (int i = 0; i < 10; i++) {

    Worker w = new Worker(cyclicBarrier);

    w.start();

    }

    cyclicBarrier.await();

    }

    这里我们假设有一个worder线程,里面有2步操作,要求所有线程完成step1后,才能继续step2. 执行结果如下:

    Thread-0 step 1 ...

    Thread-1 step 1 ...

    Thread-2 step 1 ...

    Thread-3 step 1 ...

    Thread-4 step 1 ...

    Thread-5 step 1 ...

    Thread-6 step 1 ...

    Thread-7 step 1 ...

    Thread-8 step 1 ...

    Thread-9 step 1 ...

    Thread-9 step 2 ...

    Thread-0 step 2 ...

    Thread-3 step 2 ...

    Thread-4 step 2 ...

    Thread-6 step 2 ...

    Thread-2 step 2 ...

    Thread-1 step 2 ...

    Thread-8 step 2 ...

    Thread-7 step 2 ...

    Thread-5 step 2 ...

    四、Exchanger

    如果2个线程需要交换数据,Exchanger就能派上用场了,见下面的示例:

    @Test

    public void exchangerTest() {

    Exchanger exchanger = new Exchanger<>();

    Thread t1 = new Thread(() -> {

    String temp = "AAAAAA";

    System.out.println("thread 1 交换前:" + temp);

    try {

    temp = exchanger.exchange(temp);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    System.out.println("thread 1 交换后:" + temp);

    });

    Thread t2 = new Thread(() -> {

    String temp = "BBBBBB";

    System.out.println("thread 2 交换前:" + temp);

    try {

    temp = exchanger.exchange(temp);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    System.out.println("thread 2 交换后:" + temp);

    });

    t1.start();

    t2.start();

    }

    执行结果:

    thread 1 交换前:AAAAAA

    thread 2 交换前:BBBBBB

    thread 2 交换后:AAAAAA

    thread 1 交换后:BBBBBB

    五、FutureTask/Future

    一些很耗时的操作,可以用Future转化成异步,不阻塞后续的处理,直到真正需要返回结果时调用get拿到结果

    @Test

    public void futureTaskTest() throws ExecutionException, InterruptedException, TimeoutException {

    Callable callable = () -> {

    System.out.println("很耗时的操作处理中。。。");

    Thread.sleep(5000);

    return "done";

    };

    FutureTask futureTask = new FutureTask<>(callable);

    System.out.println("就绪。。。");

    new Thread(futureTask).start();

    System.out.println("主线程其它处理。。。");

    System.out.println(futureTask.get());

    System.out.println("处理完成!");

    System.out.println("-----------------");

    System.out.println("executor 就绪。。。");

    ExecutorService executorService = Executors.newSingleThreadExecutor();

    Future future = executorService.submit(callable);

    System.out.println(future.get(10, TimeUnit.SECONDS));

    }

    执行结果:

    就绪。。。

    主线程其它处理。。。

    很耗时的操作处理中。。。

    done

    处理完成!

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

    executor 就绪。。。

    很耗时的操作处理中。。。

    done

    六、阻塞队列BlockingQueue

    阻塞队列可以在线程间实现生产者-消费者模式。比如下面的示例:线程producer模拟快速生产数据,而线程consumer模拟慢速消费数据,当达到队列的上限时(即:生产者产生的数据,已经放不下了),队列就堵塞住了。

    @Test

    public void blockingQueueTest() throws InterruptedException {

    final BlockingQueue blockingDeque = new ArrayBlockingQueue<>(5);

    Thread producer = new Thread() {

    public void run() {

    Random rnd = new Random();

    while (true) {

    try {

    int i = rnd.nextInt(10000);

    blockingDeque.put(i + "");

    System.out.println(this.getName() + " 产生了一个数字:" + i);

    Thread.sleep(rnd.nextInt(50));//模拟生产者快速生产

    } catch (InterruptedException e) {

    Thread.currentThread().interrupt();

    }

    }

    }

    };

    producer.setName("producer 1");

    Thread consumer = new Thread() {

    public void run() {

    while (true) {

    Random rnd = new Random();

    try {

    String i = blockingDeque.take();

    System.out.println(this.getName() + " 消费了一个数字:" + i);

    Thread.sleep(rnd.nextInt(10000));//消费者模拟慢速消费

    } catch (InterruptedException e) {

    Thread.currentThread().interrupt();

    }

    }

    }

    };

    consumer.setName("consumer 1");

    producer.start();

    consumer.start();

    while (true) {

    Thread.sleep(100);

    }

    }

    执行结果:

    producer 1 产生了一个数字:6773

    consumer 1 消费了一个数字:6773

    producer 1 产生了一个数字:4456

    producer 1 产生了一个数字:8572

    producer 1 产生了一个数字:5764

    producer 1 产生了一个数字:2874

    producer 1 产生了一个数字:780 # 注意这里就已经堵住了,直到有消费者消费一条数据,才能继续生产

    consumer 1 消费了一个数字:4456

    producer 1 产生了一个数字:4193

    展开全文
  • 一步步优化页面渲染功能 本节将模拟一个简单的页面渲染功能,它的作用是将HTML页面绘制到图像缓存中,为了简便,假设HTML... } interface TravelQuote { } interface TravelInfo { } 例子来自:《Java并发编程实战》

    一步步优化页面渲染功能

    本节将模拟一个简单的页面渲染功能,它的作用是将HTML页面绘制到图像缓存中,为了简便,假设HTML文件只包含标签文本以及预订大小的图片和URL。

    1、串行的页面渲染器

    最简单的实现方式是对HTML文档进行串行处理:先绘制文本,然后绘制图像,串行处理:

    public class SingleThreadRenderer {

    void renderPage(CharSequence source) {

    renderText(source);

    List imageData = new ArrayList();

    for (ImageInfo imageInfo : scanForImageInfo(source))

    imageData.add(imageInfo.downloadImage());

    for (ImageData data : imageData)

    renderImage(data);

    }

    }

    这种实现方式有个问题,因为图像下载过程的大部分时间都是在等待I/O操作执行完成,在这期间CPU几乎不做任何工作。因此,这种执行方式没有充分地利用CPU,使得用户在看到最终页面之前要等待过长时间。通过将问题分解为多个独立的任务并发执行,能够活得更高的CPU利用率和响应灵敏度。

    2、使用Future实现页面渲染器

    为了使页面渲染器实现更高的并发性,首先将渲染过程分解为两个任务,一个是渲染所有的文本,另一个是下载所有的图像(一个是CPU密集型,一个是I/O密集型)。Callable和Future有助于表示这种协同任务的交互,以下代码首先创建一个Callable来下载所有的图像,当主任务需要图像时,它会等待Future.get的调用结果。如果幸运的话,图像可能已经下载完成,即使没有,至少也已经提前开始下载。

    public class FutureRenderer {

    private final ExecutorService executor = Executors.newCachedThreadPool();

    void renderPage(CharSequence source) {

    final List imageInfos = scanForImageInfo(source);

    Callable> task =

    new Callable>() {

    public List call() {

    List result = new ArrayList();

    for (ImageInfo imageInfo : imageInfos)

    result.add(imageInfo.downloadImage());

    return result;

    }

    };

    Future> future = executor.submit(task);

    renderText(source);

    try {

    List imageData = future.get();

    for (ImageData data : imageData)

    renderImage(data);

    } catch (InterruptedException e) {

    Thread.currentThread().interrupt();

    future.cancel(true);

    } catch (ExecutionException e) {

    throw launderThrowable(e.getCause());

    }

    }

    }

    当然,我们还可以优化,用户其实不需要等待所有图像下载完成,我们可以每下载完一张图像就立刻显示出来。

    3、使用CompletionService实现页面渲染器

    要实现下载完一张就立刻绘制,我们需要及时知道图片下载完成,对于这种场景,CompletionService十分符合需求。CompletionService将生产新的异步任务与使用已完成任务的结果分离开来的服务。生产者 submit 执行的任务,使用者 take 已完成的任务,并按照完成这些任务的顺序处理它们的结果。下面的代码使用CompletionService改写了页面渲染器的实现:

    public abstract class Renderer {

    private final ExecutorService executor;

    Renderer(ExecutorService executor) {

    this.executor = executor;

    }

    void renderPage(CharSequence source) {

    final List info = scanForImageInfo(source);

    CompletionService completionService =

    new ExecutorCompletionService(executor);

    for (final ImageInfo imageInfo : info)

    completionService.submit(new Callable() {

    public ImageData call() {

    return imageInfo.downloadImage();

    }

    });

    renderText(source);

    try {

    for (int t = 0, n = info.size(); t < n; t++) {

    Future f = completionService.take();

    ImageData imageData = f.get();

    renderImage(imageData);

    }

    } catch (InterruptedException e) {

    Thread.currentThread().interrupt();

    } catch (ExecutionException e) {

    throw launderThrowable(e.getCause());

    }

    }

    }

    为任务设置时限

    有时候,如果某个任务无法在指定时间内完成,那么将不再需要它的结果,此时可以放弃这个任务。例如,某个Web应用程序从外部的广告服务器上获取广告信息,但是如果该应用程序在两秒内得不到响应,那么将显示一个默认的广告页,这样即使不能活得广告信息,也不会降低站点的响应性能,对于这种需求,Future.get方法可以实现:

    Page renderPageWithAd() throws InterruptedException {

    long endNanos = System.nanoTime() + TIME_BUDGET;

    Future f = exec.submit(new FetchAdTask());

    // Render the page while waiting for the ad

    Page page = renderPageBody();

    Ad ad;

    try {

    // Only wait for the remaining time budget

    long timeLeft = endNanos - System.nanoTime();

    ad = f.get(timeLeft, NANOSECONDS);

    } catch (ExecutionException e) {

    ad = DEFAULT_AD;

    } catch (TimeoutException e) {

    ad = DEFAULT_AD;

    f.cancel(true);

    }

    page.setAd(ad);

    return page;

    }

    这种"预订时间"的方法可以很容易地扩展到任意数量的任务上,考虑这样一个旅行网站:用户输入旅行日期及要求,网站通过多种途径获取结果,此时,不应该让页面的响应时间受限于最慢的途径,而应该只显示在指定时间内收到的消息,我们可以通过使用支持限时的invokeAll,将多个任务提交到一个ExecutorService的方式实现这个需求:

    public class TimeBudget {

    private static ExecutorService exec = Executors.newCachedThreadPool();

    public List getRankedTravelQuotes(TravelInfo travelInfo, Set companies,

    Comparator ranking, long time, TimeUnit unit)

    throws InterruptedException {

    List tasks = new ArrayList();

    for (TravelCompany company : companies)

    tasks.add(new QuoteTask(company, travelInfo));

    List> futures = exec.invokeAll(tasks, time, unit);

    List quotes =

    new ArrayList(tasks.size());

    Iterator taskIter = tasks.iterator();

    for (Future f : futures) {

    QuoteTask task = taskIter.next();

    try {

    quotes.add(f.get());

    } catch (ExecutionException e) {

    quotes.add(task.getFailureQuote(e.getCause()));

    } catch (CancellationException e) {

    quotes.add(task.getTimeoutQuote(e));

    }

    }

    Collections.sort(quotes, ranking);

    return quotes;

    }

    }

    class QuoteTask implements Callable {

    private final TravelCompany company;

    private final TravelInfo travelInfo;

    public QuoteTask(TravelCompany company, TravelInfo travelInfo) {

    this.company = company;

    this.travelInfo = travelInfo;

    }

    TravelQuote getFailureQuote(Throwable t) {

    return null;

    }

    TravelQuote getTimeoutQuote(CancellationException e) {

    return null;

    }

    public TravelQuote call() throws Exception {

    return company.solicitQuote(travelInfo);

    }

    }

    interface TravelCompany {

    TravelQuote solicitQuote(TravelInfo travelInfo) throws Exception;

    }

    interface TravelQuote {

    }

    interface TravelInfo {

    }

    例子来自:《Java并发编程实战》

    展开全文
  • 本文研究的主要是Java并发之条件阻塞Condition的应用示例代码,具体如下。Condition将Object监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象...

    本文研究的主要是Java并发之条件阻塞Condition的应用示例代码,具体如下。

    Condition将Object监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用。

    1. Condition的基本使用

    由于Condition可以用来替代wait、notify等方法,所以可以对比着之前写过的线程间通信的代码来看,再来看一下原来那个问题:

    有两个线程,子线程先执行10次,然后主线程执行5次,然后再切换到子线程执行10,再主线程执行5次……如此往返执行50次。

    之前用wait和notify来实现的,现在用Condition来改写一下,代码如下:

    public class ConditionCommunication {

    public static void main(String[] args) {

    Business bussiness = new Business();

    new Thread(new Runnable() {

    // 开启一个子线程

    @Override

    public void run() {

    for (int i = 1; i <= 50; i++) {

    bussiness.sub(i);

    }

    }

    }

    ).start();

    // main方法主线程

    for (int i = 1; i <= 50; i++) {

    bussiness.main(i);

    }

    }

    }

    class Business {

    Lock lock = new ReentrantLock();

    Condition condition = lock.newCondition();

    //Condition是在具体的lock之上的

    private Boolean bShouldSub = true;

    public void sub(int i) {

    lock.lock();

    try {

    while (!bShouldSub) {

    try {

    condition.await();

    //用condition来调用await方法

    }

    catch (Exception e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    for (int j = 1; j <= 10; j++) {

    System.out.println("sub thread sequence of " + j

    + ", loop of " + i);

    }

    bShouldSub = false;

    condition.signal();

    //用condition来发出唤醒信号,唤醒某一个

    }

    finally {

    lock.unlock();

    }

    }

    public void main(int i) {

    lock.lock();

    try {

    while (bShouldSub) {

    try {

    condition.await();

    //用condition来调用await方法

    }

    catch (Exception e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    for (int j = 1; j <= 10; j++) {

    System.out.println("main thread sequence of " + j

    + ", loop of " + i);

    }

    bShouldSub = true;

    condition.signal();

    //用condition来发出唤醒信号么,唤醒某一个

    }

    finally {

    lock.unlock();

    }

    }

    }

    从代码来看,Condition的使用时和Lock一起的,没有Lock就没法使用Condition,因为Condition是通过Lock来new出来的,这种用法很简单,只要掌握了synchronized和wait、notify的使用,完全可以掌握Lock和Condition的使用。

    2. Condition的拔高

    2.1 缓冲区的阻塞队列

    上面使用Lock和Condition来代替synchronized和Object监视器方法实现了两个线程之间的通信,现在再来写个稍微高级点应用:模拟缓冲区的阻塞队列。

    什么叫缓冲区呢?举个例子,现在有很多人要发消息,我是中转站,我要帮别人把消息发出去,那么现在我  就需要做两件事,一件事是接收用户发过来的消息,并按顺序放到缓冲区,另一件事是从缓冲区中按顺序取出用户发过来的消息,并发送出去。

    现在把这个实际的问题抽象一下:缓冲区即一个数组,我们可以向数组中写入数据,也可以从数组中把数据取走,我要做的两件事就是开启两个线程,一个存数据,一个取数据。但是问题来了,如果缓冲区满了,说明接收的消息太多了,即发送过来的消息太快了,我另一个线程还来不及发完,导致现在缓冲区没地方放了,那么此时就得阻塞存数据这个线程,让其等待;相反,如果我转发的太快,现在缓冲区所有内容都被我发完了,还没有用户发新的消息来,那么此时就得阻塞取数据这个线程。

    好了,分析完了这个缓冲区的阻塞队列,下面就用Condition技术来实现一下:

    class Buffer {

    final Lock lock = new ReentrantLock();

    //定义一个锁

    final Condition notFull = lock.newCondition();

    //定义阻塞队列满了的Condition

    final Condition notEmpty = lock.newCondition();

    //定义阻塞队列空了的Condition

    final Object[] items = new Object[10];

    //为了下面模拟,设置阻塞队列的大小为10,不要设太大

    int putptr, takeptr, count;

    //数组下标,用来标定位置的

    //往队列中存数据

    public void put(Object x) throws InterruptedException {

    lock.lock();

    //上锁

    try {

    while (count == items.length) {

    System.out.println(Thread.currentThread().getName() + " 被阻塞了,暂时无法存数据!");

    notFull.await();

    //如果队列满了,那么阻塞存数据这个线程,等待被唤醒

    }

    //如果没满,按顺序往数组中存

    items[putptr] = x;

    if (++putptr == items.length) //这是到达数组末端的判断,如果到了,再回到始端

    putptr = 0;

    ++count;

    //消息数量

    System.out.println(Thread.currentThread().getName() + " 存好了值: " + x);

    notEmpty.signal();

    //好了,现在队列中有数据了,唤醒队列空的那个线程,可以取数据啦

    }

    finally {

    lock.unlock();

    //放锁

    }

    }

    //从队列中取数据

    public Object take() throws InterruptedException {

    lock.lock();

    //上锁

    try {

    while (count == 0) {

    System.out.println(Thread.currentThread().getName() + " 被阻塞了,暂时无法取数据!");

    notEmpty.await();

    //如果队列是空,那么阻塞取数据这个线程,等待被唤醒

    }

    //如果没空,按顺序从数组中取

    Object x = items[takeptr];

    if (++takeptr == items.length) //判断是否到达末端,如果到了,再回到始端

    takeptr = 0;

    --count;

    //消息数量

    System.out.println(Thread.currentThread().getName() + " 取出了值: " + x);

    notFull.signal();

    //好了,现在队列中有位置了,唤醒队列满的那个线程,可以存数据啦

    return x;

    }

    finally {

    lock.unlock();

    //放锁

    }

    }

    }

    这个程序很经典,我从官方JDK文档中拿出来的,然后加了注释。程序中定义了两个Condition,分别针对两个线程,等待和唤醒分别用不同的Condition来执行,思路很清晰,程序也很健壮。可以考虑一个问题,为啥要用两个Codition呢?之所以这么设计肯定是有原因的,如果用一个Condition,现在假设队列满了,但是有2个线程A和B同时存数据,那么都进入了睡眠,好,现在另一个线程取走一个了,然后唤醒了其中一个线程A,那么A可以存了,存完后,A又唤醒一个线程,如果B被唤醒了,那就出问题了,因为此时队列是满的,B不能存的,B存的话就会覆盖原来还没被取走的值,就因为使用了一个Condition,存和取都用这个Condition来睡眠和唤醒,就乱了套。到这里,就能体会到这个Condition的用武之地了,现在来测试一下上面的阻塞队列的效果:

    public class BoundedBuffer {

    public static void main(String[] args) {

    Buffer buffer = new Buffer();

    for (int i = 0; i < 5; i ++) {

    //开启5个线程往缓冲区存数据

    new Thread(new Runnable() {

    @Override

    public void run() {

    try {

    buffer.put(new Random().nextint(1000));

    //随机存数据

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    ).start();

    }

    for (int i = 0; i < 10; i ++) {

    //开启10个线程从缓冲区中取数据

    new Thread(new Runnable() {

    @Override

    public void run() {

    try {

    buffer.take();

    //从缓冲区取数据

    }

    catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    ).start();

    }

    }

    }

    我故意只开启5个线程存数据,10个线程取数据,就是想让它出现取数据被阻塞的情况发生,看运行的结果:

    Thread-5 被阻塞了,暂时无法取数据!

    Thread-10 被阻塞了,暂时无法取数据!

    Thread-1 存好了值: 755

    Thread-0 存好了值: 206

    Thread-2 存好了值: 741

    Thread-3 存好了值: 381

    Thread-14 取出了值: 755

    Thread-4 存好了值: 783

    Thread-6 取出了值: 206

    Thread-7 取出了值: 741

    Thread-8 取出了值: 381

    Thread-9 取出了值: 783

    Thread-5 被阻塞了,暂时无法取数据!

    Thread-11 被阻塞了,暂时无法取数据!

    Thread-12 被阻塞了,暂时无法取数据!

    Thread-10 被阻塞了,暂时无法取数据!

    Thread-13 被阻塞了,暂时无法取数据!

    从结果中可以看出,线程5和10抢先执行,发现队列中没有,于是就被阻塞了,睡在那了,直到队列中有新的值存入才可以取,但是它们两运气不好,存的数据又被其他线程给抢先取走了,哈哈……可以多运行几次。如果想要看到存数据被阻塞,可以将取数据的线程设置少一点,这里我就不设了。

    2.2 两个以上线程之间的唤醒

    还是原来那个题目,现在让三个线程来执行,看一下题目:

    有三个线程,子线程1先执行10次,然后子线程2执行10次,然后主线程执行5次,然后再切换到子线程1执行10次,子线程2执行10次,主线程执行5次……如此往返执行50次。

    如过不用Condition,还真不好弄,但是用Condition来做的话,就非常方便了,原理很简单,定义三个Condition,子线程1执行完唤醒子线程2,子线程2执行完唤醒主线程,主线程执行完唤醒子线程1。唤醒机制和上面那个缓冲区道理差不多,下面看看代码吧,很容易理解。

    public class ThreeConditionCommunication {

    public static void main(String[] args) {

    Business bussiness = new Business();

    new Thread(new Runnable() {

    // 开启一个子线程

    @Override

    public void run() {

    for (int i = 1; i <= 50; i++) {

    bussiness.sub1(i);

    }

    }

    }

    ).start();

    new Thread(new Runnable() {

    // 开启另一个子线程

    @Override

    public void run() {

    for (int i = 1; i <= 50; i++) {

    bussiness.sub2(i);

    }

    }

    }

    ).start();

    // main方法主线程

    for (int i = 1; i <= 50; i++) {

    bussiness.main(i);

    }

    }

    static class Business {

    Lock lock = new ReentrantLock();

    Condition condition1 = lock.newCondition();

    //Condition是在具体的lock之上的

    Condition condition2 = lock.newCondition();

    Condition conditionMain = lock.newCondition();

    private int bShouldSub = 0;

    public void sub1(int i) {

    lock.lock();

    try {

    while (bShouldSub != 0) {

    try {

    condition1.await();

    //用condition来调用await方法

    }

    catch (Exception e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    for (int j = 1; j <= 10; j++) {

    System.out.println("sub1 thread sequence of " + j

    + ", loop of " + i);

    }

    bShouldSub = 1;

    condition2.signal();

    //让线程2执行

    }

    finally {

    lock.unlock();

    }

    }

    public void sub2(int i) {

    lock.lock();

    try {

    while (bShouldSub != 1) {

    try {

    condition2.await();

    //用condition来调用await方法

    }

    catch (Exception e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    for (int j = 1; j <= 10; j++) {

    System.out.println("sub2 thread sequence of " + j

    + ", loop of " + i);

    }

    bShouldSub = 2;

    conditionMain.signal();

    //让主线程执行

    }

    finally {

    lock.unlock();

    }

    }

    public void main(int i) {

    lock.lock();

    try {

    while (bShouldSub != 2) {

    try {

    conditionMain.await();

    //用condition来调用await方法

    }

    catch (Exception e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    for (int j = 1; j <= 5; j++) {

    System.out.println("main thread sequence of " + j

    + ", loop of " + i);

    }

    bShouldSub = 0;

    condition1.signal();

    //让线程1执行

    }

    finally {

    lock.unlock();

    }

    }

    }

    }

    代码看似有点长,但是是假象,逻辑非常简单。关于线程中的Condition技术就总结这么多吧。

    总结

    以上就是本文关于Java并发之条件阻塞Condition的应用代码示例的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

    展开全文
  • 主类:MultiThread,执行并发类package java8test;import java.util.ArrayList;import java.util.List;import java.util.concurrent.BlockingQueue;import java.util.concurrent.Callable;import java.util....
  • java 并发例子-并行数据处理

    千次阅读 2017-04-19 23:57:17
    JDK5引进了一个高级并发的API,它位于java.util.concurrent这个包中。 它考虑到了更优雅和直观的多线程编程。我知道这对有些人来说并不算什么新消息,但是...在这个系列的帖子中我会提供一些例子教你怎么用java.uti
  • Condition主要用await和signal,跟Object的wait、notify...这边复制一下之前的wait和notif例子修改一下示例public class ConditionDemo {Lock lock = new ReentrantLock();Condition condition = lock.newConditi...
  • Java内存模型规定了所有的变量都存储在主内存中。每条线程中还有自己的工作内存,线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来)。线程对变量的所有操作(读取,赋值)都必须在工作...
  • java多线程并发例子

    2016-08-30 13:53:00
    public static void main(String[] args) { for(Thread t:getThreads()){ t.start(); } } public static Thread[] getThreads(){ Thread[] thread = new Thread[10]; for(int i=0;i<...
  • Java并发任务处理例子

    2014-02-12 16:06:13
      public void init() { ... this.ioThreadPool = new ThreadPoolExecutor(50, 50, Long.MAX_VALUE, TimeUnit.SECONDS, new java.util.concurrent.LinkedTransferQueue&lt;Runnable&gt;(), new T...
  • 日志服务需要提供的功能有: 可以从外部安全地开启和关闭日志服务; 可以供多个线程安全地记录日志消息; 在日志服务关闭后,可以把剩余未记录的消息写入日志文件;... 参考资料 《Java并发编程实战》
  • Java并发编程实践中(中+英+例子源码)
  • Java并发

    2017-04-30 15:41:00
    目录 CyclicBarrier CountDownLatch Semaphore FutureTask 锁机制 ...标签(空格分隔): Java 并发 多线程 本文中某些例子参考了鸿洋大神的博客的java并发专题,有需要的可以去看看~~~ Cyclic...
  • Java并发编程文章系列Java并发编程实战 01并发编程的Bug源头Java并发编程实战 02Java如何解决可见性和有序性问题Java并发编程实战 03互斥锁 解决原子性问题前提在第三篇文章最后的例子当中,需要获取到两个账户的锁...
  • Java多线程程序可能会遇到死锁状况,因为synchronized关键字会导致执行线程在等待与指定对象相关联的锁定或监视时出现阻止情况。 看看下面一个例子。示例public class TestThread {public static Object Lock1 = ...
  • Java并发工具类CountDownLatch源码中的例子 实例一 原文描述 /** * <p><b>Sample usage:</b> Here is a pair of classes in which a group * of worker threads use two countdown latches: * ...
  • import java.net.HttpURLConnection; import java.net.URLConnection; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.CompletableFuture; import ...
  • 最近在看《java并发编程实战》,希望自己有毅力把它读完。线程本身有很多优势,比如可以发挥多处理器的强大能力、建模更加简单、简化异步事件的处理、使用户界面的相应更加灵敏,但是更多的需要程序猿面对的是安全性...
  • 下面是一个具有同步功能的多线程示例,这是和上篇文章同样的例子,它依次打印计数器值,每次运行它时,它产生相同的结果。实例class PrintDemo {public void printCount() {try {for(int i = 5; i > 0; i--) {...
  • java多线程并发编程例子

    热门讨论 2012-03-08 12:11:38
    关于java.util.concurrent多线程核心包内各种线程资源的使用场景例子
  • java并发之CopyOnWirteArrayListCopyOnWirteArrayList的实现它用了ReentrantLock保证了add,set,remove操作的安全,同时使用volatile定义内部数组保证了可见性,之所以叫CopyOnWrite就是因为很多方法都是通过Array....
  • impport java.util.concurrent.locks.ReentranLock; public class SingletonDemo { private static SingletonDemo instance; private static ReentrantLock lock = new ReentrantLock(); private SingletonDemo...
  • 写在最前面在上文java并发之volatile末尾有提到,volatile并不能保证++操作的线程安全。我们来通过一个简单的例子看下为什么。++测试demo通过javap -v看下其反编译后字节码指令:反编译结果从反编译结果可以看出,++...
  • 跟着作者的65节课彻底搞懂Java并发原理专栏,一步步彻底搞懂Java并发原理。 作者简介:笔名seaboat,擅长工程算法、人工智能算法、自然语言处理、架构、分布式、高并发、大数据和搜索引擎等方面的技术,大多数编程...
  • 1.并发和并行举个例子并发(concurrency):并发的关键在于有处理多个任务的能力,但并不一定就是同时处理.例1: 吃饭时,电话响起,暂时停下吃饭的动作,接完电话后继续吃饭.例2: 一个人吃3个馒头并行(parallelism):并行的...
  • JAVA并发编程与高并发解决方案 - 并发编程 六版本作者内容2018.7.4chuIllusions线程池相关文章线程池在前面使用的例子用,我们已经使用过线程池,基本上就是初始化线程池实例之后,把任务丢进去,等待调度执行就可以...
  • JAVA 并发

    2019-09-16 08:25:27
    java5以后, synchronized和wait,notify; 改为了Lock和Condition.await,Condition.signal semaphore 信号灯工具 维护当前访问的线程数;使用例子:能控制访问的最大线程数 cyclicbarrie...
  • java并发卖票例子

    千次阅读 2018-04-29 21:49:14
    package ... import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.Vector; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.co...
  • java 并发学习总结

    2016-04-06 09:20:49
    java并发学习总结 例子:包含(并发容器、同步容器、同步工具、死锁、异常、中断、线程池、返回结果、同步方法等代码例子

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,023
精华内容 1,209
关键字:

java并发例子

java 订阅