java多线程之同步计数器

同步计数器之--CountDownLatch

    使用场景:当用户上传文件,我们可以多开几个线程来上传文件,当所有的线程上传完成后,文件才算上传完成,在此场景下可以使用CountDownLatch,当每个线程上传完成,调用CountDownLatch.countDown()使计数器减1,当计数器为0时表示文件上传完成。

    CountDownLatch的同步器是通过AQS来实现同步的,在CountDownLatch内部定义了一个Sycn类,此类继承AQS来实现同步,我们来看下CountDownLatch的几个主要方法的执行过程

    1).当CountDownLatch调用countDown()时

//CountDownLatch内部调用静态内部类Sync的方法,但是Sync并没有releaseShared(int i)的实现,所以我们查看它的父类AQS
public void countDown() {
        sync.releaseShared(1);
    }

AbstractQueuedSynchronizer类

//在AQS中调用子类的tryReleaseShared(int i)的方法,如果子类的方法返回true,则 doReleaseShared(),并且自身返回true
public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();//释放信号量
            return true;
        }
        return false;
    }

Sync类

//
protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();//获取当前计数器
                if (c == 0)//如果计数器为0,则直接返回,因为计数器为0,则释放所有线程,CountDownLatch工作已经结束了
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))//原子性的将计数器减1,返回计数器是否==0
                    return nextc == 0;
            }
        }

2).当CountDownLatch调用await()时

//CountDownLatch调用Sync类中的acquireSharedInterruptibly(int i)方法,Sync中没有实现,所以是调用AQS的方法()
public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

AQS类

//调用子类的tryAcquireShared(int i)方法
public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())//判断线程是否中断
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)//结合下面的代码可以看出,当计数器不等于0时,往下执行,
            doAcquireSharedInterruptibly(arg);
    }
private void doAcquireSharedInterruptibly(int arg)//这段有点看不懂啊
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);//获取当前信号量是否为0,如果为0这个方法返回1,否则返回-1
                    if (r >= 0) {//当信号量为0的时候就退出死循环,否则一直在这里面出不去,所以实现了一直等待
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
Sync类

//当计数器等于0时返回1,否则返回-1;
protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

CountDownLatch示例

package com.synchronize;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 同步计数器
 * @author lijh
 *
 */
public class MyCountDownLatch {
	 
	public static void main(String[] args) {
	     CountDownLatch doneSignal = new CountDownLatch(5);
	     //定长线程池
	     ExecutorService pool = Executors.newFixedThreadPool(1);
	     for (int i = 0; i < 5; ++i){
	    	 pool.execute(new WorkerRunnable(doneSignal, i)); 
	     } 
	    	
	     try {
	    	 //在计数器为0之前会一直等待
			 doneSignal.await();
		 } catch (InterruptedException e) {
			 e.printStackTrace();
		 } 
	     //关闭线程池
	     pool.shutdown();
	     
	     System.out.println("main 线程结束");
	}
}

class WorkerRunnable implements Runnable {
    private final CountDownLatch doneSignal;
    private final int i;
    WorkerRunnable(CountDownLatch doneSignal, int i) {
       this.doneSignal = doneSignal;
       this.i = i;
    }
    public void run() {
    	//每个线程做完自己的事情后,计数器减1
        doWork(i);
        doneSignal.countDown();
        Thread.currentThread().interrupt();
    }
    
    void doWork(int i) {
	    try {
		    TimeUnit.SECONDS.sleep(1);
		    System.out.println(doneSignal.getCount());
	    } catch (InterruptedException e) {
		    e.printStackTrace();
	    }
	}
}

同步计数器之--Semaphore

semaphore通常用于限制访问一些资源的线程数目,比如有一个服务器,同时最多只能三个人访问,此时可以使用semaphore实现

Semaphore方法比较多,此处展示一些常用的


Semaphore示例

package com.synchronize;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
 * Semaphore同步器会规定一个阀值来限制对资源的访问,使用资源时,线程最多不能超出这个阀值
 * 场景:当前驾校有2辆车,有5个学员练车,每次只有2个学员可以练车,
 * 其他学员需要等待,当有学员练完车了,可以让给下一个学员练车
 * @author lijh
 *
 */
public class MySemaphore {
	
	public static void main(String[] args) {
		//最多两个线程访问资源
		Semaphore sp = new Semaphore(2);
		//创建一个容纳5个线程的线程池
		ExecutorService pool = Executors.newFixedThreadPool(5);
		Driver d = new Driver(sp);
		Car c1 = new Car(d);
		Car c2 = new Car(d);
		Car c3 = new Car(d);
		Car c4 = new Car(d);
		Car c5 = new Car(d);
		pool.execute(c1);
		pool.execute(c2);
		pool.execute(c3);
		pool.execute(c4);
		pool.execute(c5);
		
		pool.shutdown();//关闭线程池
	}

}
//车
class Car implements Runnable{
	
	private Driver d;
	
	public Car(Driver d){
		this.d = d;
	}

	@Override
	public void run() {
		d.driverCar();
	}
}
//司机
class Driver{
	//同步计数器
	private Semaphore sp;
	
	public Driver(Semaphore sp){
		this.sp = sp;
	}
	//学员练车
	public void driverCar(){
		try {
			sp.acquire();//从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
			System.out.println(Thread.currentThread().getName()+" start");
			TimeUnit.SECONDS.sleep(2);
			System.out.println(Thread.currentThread().getName()+" end");
			sp.release();释放一个许可,将其返回给信号量。
			//Thread.currentThread().interrupt();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}

同步计数器之--CyclicBarrier

先看一个示例

package com.synchronize;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
 * CyclicBarrier 允许一组线程互相等待,直到到达某个公共屏障点,然后所有线程继续往后执行。
 * CyclicBarrier 与 CountDownLatch 区别在于 CyclicBarrier 是多个线程互相等待,
 * 			CountDownLatch 是一个线程等待多个线程完成工作,等待的对象不同了
 * 场景:公司有4个人,每天早上开晨会时,大家一起去会议室,当所有人都到达会议室,会议开始。
 * 
 * @author lijh
 *
 */
public class MyCyclicBarrier {

	public static void main(String[] args) throws InterruptedException {
		
		CyclicBarrier cb = new CyclicBarrier(4,new Thread(new Meeting()));
		
		ExecutorService pool = Executors.newFixedThreadPool(4);
		Member s1 = new Member(cb);
		Member s2 = new Member(cb);
		Member s3 = new Member(cb);
		Member s4 = new Member(cb);
		
		pool.execute(s1);
		pool.execute(s2);
		pool.execute(s3);
		//pool.execute(s4);
		TimeUnit.SECONDS.sleep(5);
		//当CyclicBarrier.await超时时,会抛出异常
		pool.execute(s4);
		pool.shutdown();
	}
}
/**
 * 成员
 * @author lijh
 *
 */
class Member implements Runnable{

	private CyclicBarrier cb;
	
	public Member(CyclicBarrier cb){
		this.cb = cb;
	}
	
	@Override
	public void run() {
			try {
				Thread.sleep(1000);
				System.out.println("成员 "+Thread.currentThread().getName()+" 已经到达会议室!");
				cb.await(3,TimeUnit.SECONDS);
				//cb.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			} catch (TimeoutException e) {
				e.printStackTrace();
			}
	}
	
}
/**
 * 会议
 * @author lijh
 *
 */
class Meeting implements Runnable{

	@Override
	public void run() {
		System.out.println("会议开始!");
	}
	
}

CyclicBarrier有两个构造方法

    CyclicBarrier(int i) 和CyclicBarrier(int i , Runnable r),第二个构造,是在所有参与者都在此屏障上后,会优先执行r这个线程,上面的示例也演示出了这一点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值