java并发:线程同步机制之计数器&Exechanger

第一节 CountDownLatch

(1)初识CountDownLatch

(2)详述CountDownLatch

  CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1,当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

CountDownLatch中主要方法如下:

  public CountDownLatch(int count),构造函数中的count(计数器)实际上就是闭锁需要等待的线程数量,这个值只能被设置一次,而且CountDownLatch没有提供任何机制去重新设置这个计数值。

  public void countDown(),每调用一次这个方法,在构造函数中初始化的count值就减1,通知机制是此方法来完成的。

  public void await() throws InterruptedException,调用此方法的当前线程会一直阻塞,直到计时器的值为0。

(3)CountDownLatch示例

package com.test;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo{

    public static void main(String args[]) throws Exception{
        CountDownLatch latch = new CountDownLatch(3);
        Worker worker1 = new Worker("Jack 程序员1",latch);
        Worker worker2 = new Worker("Rose 程序员2",latch);
        Worker worker3 = new Worker("Json 程序员3",latch);
        worker1.start();
        worker2.start();
        worker3.start();

        latch.await();
        System.out.println("Main thread end!");
    }

    static class Worker extends Thread {
        private String workerName;
        private CountDownLatch latch;
        public Worker(String workerName,CountDownLatch latch) {
            this.workerName = workerName;
            this.latch = latch;
        }
        @Override
        public void run() {
            try {
                System.out.println("Worker:"+workerName +" is begin.");
                Thread.sleep(1000L);
                System.out.println("Worker:"+workerName +" is end.");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }//模仿干活;
            latch.countDown();
        }
    }
}

上述程序运行结果如下:

Worker:Rose 程序员2 is begin.
Worker:Json 程序员3 is begin.
Worker:Jack 程序员1 is begin.
Worker:Jack 程序员1 is end.
Worker:Json 程序员3 is end.
Worker:Rose 程序员2 is end.
Main thread end!

从结果上可以看出,MainThread执行到latch.await();处会阻塞在该处,直到三个线程均完成的时候MainThread才会继续往下执行

(4)参考资料

本小节只是简单描述了CountDownLatch的使用方式等,欲了解其实现机制,可以查看下面的几篇文章

A、http://blog.itpub.net/30024515/viewspace-1432825/

B、http://www.tuicool.com/articles/mQnAfq

第二节 CyclicBarrier

(1)初识CyclicBarrier

(2)CyclicBarrier示例

应用场景:在某种需求中,比如一个大型的任务,常常需要分配很多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候就可以选择CyclicBarrier了。

示例:

package com.test;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo{

    public static void main(String args[]) throws Exception{

        CyclicBarrier barrier = new CyclicBarrier(3,new TotalTask());

        BillTask worker1 = new BillTask("111",barrier);
        BillTask worker2 = new BillTask("222",barrier);
        BillTask worker3 = new BillTask("333",barrier);
        worker1.start();
        worker2.start();
        worker3.start();
        System.out.println("Main thread end!");
    }

    static class TotalTask extends Thread {
public void run() {
            System.out.println("所有子任务都执行完了,就开始执行主任务了。");
        }
    }

    static class BillTask extends Thread {
        private String billName;
        private CyclicBarrier barrier;
        public BillTask(String workerName,CyclicBarrier barrier) {
            this.billName = workerName;
            this.barrier = barrier;
        }
        @Override
        public void run() {
            try {
                System.out.println("市区:"+billName +"运算开始:");
                Thread.sleep(1000L);//模仿第一次运算;
                System.out.println("市区:"+billName +"运算完成,等待中...");
                barrier.await();//假设一次运算不完,第二次要依赖第一次的运算结果。都到达这个节点之后后面才会继续执行;
                System.out.println("全部都结束,市区"+billName +"才开始后面的工作。");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

}

上述程序运行结果如下:

市区:111运算开始:
市区:333运算开始:
Main thread end!
市区:222运算开始:
市区:333运算完成,等待中...
市区:222运算完成,等待中...
市区:111运算完成,等待中...
所有子任务都执行完了,就开始执行主任务了。//这句话是最后到达wait()方法的那个线程执行的
全部都结束,市区111才开始后面的工作。
全部都结束,市区222才开始后面的工作。
全部都结束,市区333才开始后面的工作。

解说:在这个示例中,构造CyclicBarrier时,传入了内部类TotalTask(TotalTask继承了Thread,是Runnable的实现)的实例对象,其意义在于:当所有的线程都执行到wait()方法时,它们会一起返回继续自己的工作,但是最后一个到达wait()方法的线程会执行TotalTask的run()方法;如果在构造构造CyclicBarrier时没有传入Runnable的实现对象作为构造参数,则当所有的线程都执行到wait()方法时会直接一起返回继续自己的工作。

(3)CyclicBarrier与CountDownLatch的区别

A、CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待;
B、CountDownLatch的计数器无法被重置;而CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。

第三节 Semaphore

(1)初识Semaphore

  Java中的Semaphore用于在线程间传递信号,从概念上讲,信号量维护了一个许可集合,Semaphore只对可用的许可进行计数,并采取相应的行动。信号量常常用于多线程的代码中,比如数据库连接池。

(2)Semaphore示例

场景:假设一个服务器资源有限,任意某一时刻只允许3个人同时进行访问,这时一共来了10个人

package com.test;
import java.util.concurrent.Semaphore;
public class SemaphoreDemo{

    public static void main(String args[]) throws Exception{

        final Semaphore semaphore = new Semaphore(3);//一次只运行3个人进行访问

        for(int i=0;i<10;i++) {
            final int no = i;
            Runnable thread = new Runnable() {
                public void run (){
                    try {
                        System.out.println("用户"+no+"连接上了:");
                        Thread.sleep(300L);
                        semaphore.acquire();//获取接下去执行的许可
                        System.out.println("用户"+no+"开始访问后台程序...");
                        Thread.sleep(1000L);//模仿用户访问服务过程
                        semaphore.release();//释放允许下一个线程访问进入后台
                        System.out.println("用户"+no+"访问结束。");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            new Thread(thread).start();
        }

        System.out.println("Main thread end!");
    }
}

上述代码运行结果如下:

用户1连接上了:
用户3连接上了:
用户4连接上了:
用户2连接上了:
用户0连接上了:
用户5连接上了:
用户7连接上了:
Main thread end!
用户6连接上了:
用户8连接上了:
用户9连接上了:
用户3开始访问后台程序...
用户4开始访问后台程序...
用户2开始访问后台程序...
用户4访问结束。
用户3访问结束。
用户7开始访问后台程序...
用户0开始访问后台程序...
用户8开始访问后台程序...
用户2访问结束。
用户5开始访问后台程序...
用户0访问结束。
用户7访问结束。
用户1开始访问后台程序...
用户8访问结束。
用户6开始访问后台程序...
用户1访问结束。
用户9开始访问后台程序...
用户5访问结束。
用户6访问结束。
用户9访问结束。

从结果上可以看出来,10个人同时进来,但是只能同时3个人访问资源,释放一个允许进来一个

(3)参考资料

http://ifeve.com/semaphore/

第四节 Exchanger

(1)初识Exchanger

此处的Exechanger与前面描述的几个同步机制不一样,前面描述的几个同步机制均是通过计数器来实现的,下面简单描述一下Exechanger,看看Exchanger的应用场景:

注意:从上文描述,我们知道Exchanger用于在成对出现的线程之间(两个线程共有一个Exchanger)交换数据

(2)Exechanger示例

(3)参考资料

http://www.cnblogs.com/davidwang456/p/4179488.html

时间: 2024-10-18 21:39:30

java并发:线程同步机制之计数器&Exechanger的相关文章

Java 并发 线程同步

Java 并发 线程同步 @author ixenos 同步 1.异步线程本身包含了执行时需要的数据和方法,不需要外部提供的资源和方法,在执行时也不关心与其并发执行的其他线程的状态和行为 2.然而,大多数实际的多线程应用中,两个或两个以上的线程需要共享对同一数据的存取,这将产生同步问题(可见性和同步性的丢失) 比如两个线程同时执行指令account[to] += amount,这不是原子操作,可能被处理如下: a)将account[to]加载到寄存器 b)增加amount c)将结果写回acco

java的线程同步机制synchronized关键字的理解

线程同步:               由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问. 需要明确的几个问题: 1)synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块.如果 再细的分类,synchronized可作用于instance变量.object reference(对象引用).static函数和clas

【总结】Java线程同步机制深刻阐述

原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread)是一份独立运行的程序,有自己专用的运行栈.线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等. 当多个线程同时读写同一份共享资源的时候,可能会引起冲突.这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团. 同步这个词是从英文synchronize

线程同步机制(二)-- 线程同步辅助类

我们在线程同步机制(一)--Synchronized和Lock简要介绍中学习了同步和临界区的概念,并且讨论了多个并发任务共享一个资源时的同步情况.访问共享资源的代码块叫临界区. 我们在线程同步机制(一)--Synchronized和Lock简要介绍中学习了一下内容: synchronized关键字 Lock接口及其实现类,如ReentrantLock,ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock 本章我们将学习如

Java多线程的同步机制(synchronized)

一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池 等待队列中). 取到锁后,他就开始执行同步代码(被synchronized修饰的代码):线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中 等待的某个线程就可以拿到锁执行同步代码了.这样就保证了同步代码在统一时刻只有一个线程在执行. 众所周知,在Java多线程

线程同步机制(一)--Synchronized,Lock

多个执行线程共享一个资源的情形是最常见的并发编程情景之一.在并发应用中常常遇到这样的情景:多个线程读或者写相同的数据,或者访问相同的文件或者数据库连接.为了防止这些共享资源可能出现错误或者数据不一致,人们引入了临界区(critical section)概念.临界区是一个用以访问共享资源的代码块,这个代码块中同一时间只允许一个线程执行. 为了实现这个临界区,Java提供了同步机制.当一个线程试图访问一个临界区时,它将使用一种同步机制来查看是不是已经有其他线程进入临界区.如果没有,它就进入临界区,如

java并发系列-monitor机制实现

背景 在jdk1.6以前synchronized的java内置锁不存在 偏向锁->轻量级锁->重量级锁 的锁膨胀机制,锁膨胀机制是1.6之后为了优化java线程同步性能而实现的.而1.6之前都是基于monitor机制的重量级锁.因为java内部对锁实现的封装,就算现在我们也只需要了解重量级锁就可以了.深入了解monitor机制对学习线程同步非常重要. 正文 目录 什么是monitor monitor的作用 monitor的组成 寻找monitor锁 java monitor机制的实现 什么是m

Java 并发 线程属性

Java 并发 线程属性 @author ixenos 线程优先级 1.每当线程调度器有机会选择新线程时,首先选择具有较高优先级的线程 2.默认情况下,一个线程继承它的父线程的优先级 当在一个运行的线程A里,创建另一个线程B的时候,那么A是父线程,B是子线程.当在一个运行的线程A里,创建线程B,然后又创建了线程C,这时候虽然B比C创建早,可是B并不是C的父线程,而A是B和C的父线程. 3.线程的优先级高度依赖于系统,当虚拟机依赖于宿主机平台的线程实现机制时,Java线程的优先级被映射到宿主机平台

Java中线程同步的理解 - 其实应该叫做Java线程排队

Java中线程同步的理解 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread)是一份独立运行的程序,有自己专用的运行栈.线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等. 当多个线程同时读写同一份共享资源的时候,可能会引起冲突.这时候,我们需要引入线程"同步"机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团. 同步这个词是从英文synchronize(使同时发生)翻译过来的.我也不明白为什么