线程阻塞与多线程计数器

一、实现线程阻塞的几种方法

主线程中开启子线程,主线程与子线程的执行顺序是不可控的,如果需要控制顺序,可以使用以下方法:

1、Callable future.get() 取数据

submit(Callable) 方法提交任务后一般还有个取数据的过程,在从Future取数据的过程中,Callable自带的阻塞机制保证主线程一定在子线程结束之后结束。注意如果没有取数据,子线程可能会在主线程结束之后才结束。具体见线程池(四)----ThreadPoolTaskExecutor的两种提交方法_threadpooltaskexecutor.execute-CSDN博客

2、CountDownLatch
3、join方法

二、线程计数器

@RestController
public class TestController {

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @RequestMapping("/test")
    public void test(){
        System.out.println("开始");
        //逻辑代码开始
        taskExecutor.execute(()->{
            //子线程逻辑
            try {
                Thread.sleep(3000);
                System.out.println("逻辑代码执行完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //逻辑代码结束
        System.out.println("结束");
    }
}

这时候子线程和主线程哪个先结束是不定的:

如果希望在子线程结束后输出成功标志,就需要有一个标志,如果开启了多个子线程,则需要有一个计数器。实现方法大致有以下几种:

1、CountDownLatch:

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

@RestController
public class TestController {

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @RequestMapping("/test")
    public void test() throws InterruptedException {
        System.out.println("开始");
        //逻辑代码开始
        int total = 10;
        final CountDownLatch mainTask = new CountDownLatch(total);
        for(int i=0;i{
                //子线程逻辑
                try {
                    Thread.sleep(3000);
                    System.out.println("逻辑代码执行完成");
                    mainTask.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        //逻辑代码结束
        mainTask.await(); // 等待所有子线程执行完毕
        System.out.println("结束");
    }
}

结果为:

线程阻塞与多线程计数器_第1张图片

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

执行结果:

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 galgameStack = new Stack();
    // 初始化栈
    for(int i=0; i 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通关了!
 

你可能感兴趣的:(多线程编程,java,spring,eclipse)