java多线程执行计数相关类

    在写java程序的时候,线程是经常用到的技术,比如事件分发、消息分发等。线程的执行不会造成主线程的阻塞,可以在后台自己单独的执行,但是有时候我们会需要知道后台的线程是否执行完毕,然后根据线程执行情况决定是否进行下一步的操作,可以在某一线程执行完毕后改变主线程中的标识,然后主线程实时的监控标识的变化。

java提供了几个现成的类用于多个线程执行时,各个线程执行进度的管理。

1、CountDownLatch

该类通过该计数的原理控制线程的执行,两个主要方法:await,countDown
初始化的时候需要传递一个参数count,记录需要执行的子线程的总数,每一个子线程执行完毕后调用countDown后计数减1,当计数为0的时候,释放自己await的线程。

void testCountDownLatch() throws Exception {
    int count = 5;
    final CountDownLatch mainTask = new CountDownLatch(count);

    for(int i=0; i<count; i++) {
        new Thread(){
            public void run() {
                try {
                    System.out.println("Thread " + Thread.currentThread().getId() + " 执行完毕!");
                    mainTask.countDown(); // 计数减一
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }

    mainTask.await(); // 等待所有子线程执行完毕

    System.out.println("所有线程执行完毕!");
}

2、CyclicBarrier

使用场景:评委打分!
表演折表演完毕后,主持人说了一句:评委们请打分!
然后评委们各自想着刚才的表演,认真的在自己的牌子上写下了分数放在桌子上,等待着其他的评委。
主持人看到所有评委都写下分了,就说请评委们亮出自己的打分牌,然后评委们就亮出了自己的牌子。
至此,程序执行完毕。

和CountDownl的区别是,此处是各个子线程之间相互等待,在所有线程都执行完之后,各个子线程在继续执行。

void testCyclicBarrier() throws Exception {
    int parties = 5;
    final CyclicBarrier cb = new CyclicBarrier(parties, new Thread(){
        @Override
        public void run() {
            System.out.println("请评委们亮出自己的打分牌!");
        }
    });
    for(int i=0; i<parties; i++) {
        new Thread(){
            public void run() {
                try {
                    System.out.println("Thread " + Thread.currentThread().getId() + " 打分完毕!");
                    cb.await();
                    System.out.println("Thread " + Thread.currentThread().getId() + " 亮出了自己牌子!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }
}

执行结果:

Thread 10 打分完毕!
Thread 11 打分完毕!
Thread 12 打分完毕!
Thread 13 打分完毕!
Thread 14 打分完毕!
请评委们亮出自己的打分牌!
Thread 14 亮出了自己牌子!
Thread 13 亮出了自己牌子!
Thread 10 亮出了自己牌子!
Thread 12 亮出了自己牌子!
Thread 11 亮出了自己牌子!

3、Semaphore

使用场景:数据库连接池。
初始化一定数量的连接池,每次请求连接的时候从连接池里取(acquire),如果连接池里没有了则放置请求,当连接池里再次出现连接的时候(有人release)返回给刚才的请求者。

int permits = 5;
int num = permits;
void testSemaphore() throws Exception {
    final Stack<Integer> galgameStack = new Stack<Integer>();
    // 初始化栈
    for(int i=0; i<permits; i++) {
        galgameStack.push(permits-i);
    }

    final Semaphore semaphore = new Semaphore(permits);

    // 生产galgame
    new Thread(){
        @Override
        public void run() {
            try {
                while(num < 10) {
                    Thread.sleep(1000);
                    galgameStack.push(num);
                    System.out.println("第" + num + "部galgame下载完毕!");
                    num ++;
                    semaphore.release();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }.start();

    // 消耗galgame
    while(true) {
        if(num > 9) {
            break;
        }
        semaphore.acquire(); // 会阻塞
//      semaphore.tryAcquire(); // 不会阻塞,如果没有了则返回false
        System.out.println("第" + galgameStack.pop() + "部galgame通关了!");

    }

}

输出结果:

第1部galgame通关了!
第2部galgame通关了!
第3部galgame通关了!
第4部galgame通关了!
第5部galgame通关了!
第5部galgame下载完毕!
第5部galgame通关了!
第6部galgame下载完毕!
第6部galgame通关了!
第7部galgame下载完毕!
第7部galgame通关了!
第8部galgame下载完毕!
第8部galgame通关了!
第9部galgame下载完毕!
第9部galgame通关了!

写在最后,本文中为了省事没有使用ExecutorService来执行线程。但在工作中最好使用

ExecutorService service = Executors.newFixedThreadPool(5);
for(int i=0; i<10; i++)
    service.execute(new Thread() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getId());
        }
    });
输出为:
10
14
12
14
12
14
10
12
16
18

这种方式来执行多个线程,这样能避免频繁创建线程消耗资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值