Java多线程模拟运动比赛

Java的并发包中有各种并发类,今天通过这个例子来熟悉使用CyclicBarrier类。

CyclicBarrier类就像一个起跑线,定义了赛道的大小,当所有运动员(多线程)同时在起跑线,

那么CyclicBarrier就会触发起跑动作。

以下是Java使用CyclicBarrier模拟运动比赛的例子

新建一个gradle工程,目录结构如下

在build.gradle中引入spring和lombok两个架包

plugins {
    id 'java'
}

group 'com.drama'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    maven {
        url 'http://179.179.179.46:8087/repository/maven-public/'
    }
}

dependencies {
    compile("org.springframework:spring-context:4.1.6.RELEASE")
    compile("org.projectlombok:lombok:1.18.8")
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

定义配置类MyConfig,定义线程池的大小,这里定为6

package demo.cyclicbarrier;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
@ComponentScan("demo.cyclicbarrier")
@EnableAsync
public class MyConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(6);    //这里的CorePoolSize一定要大于6(1个干扰者,5个运动员)
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}

定义运动比赛类型和顺序

package demo.cyclicbarrier;

import lombok.Getter;

public enum SportEnum {
    Swim("游泳"),Run("跑步"),Cycle("骑车");
    @Getter
    private final String sportName;

    SportEnum(String sportName) {
        this.sportName = sportName;
    }

    public SportEnum nextSport(){
        if(this == Swim)
            return Run;
        else if(this == Run)
            return Cycle;
        else
            return Swim;
    }
}

定义MySignal,用CyclicBarrier表示起跑线,用SportEnum表示比赛状态

package demo.cyclicbarrier;

import lombok.Getter;
import lombok.Synchronized;

import java.util.concurrent.CyclicBarrier;

public class MySignal {
    @Getter
    private final CyclicBarrier startLine;
    @Getter
    private SportEnum sport;

    private String winner;

    public MySignal(int size) {
        this.startLine = new CyclicBarrier(size,new StartLine(this));
        sport = SportEnum.Swim;
    }

    @Synchronized
    public void nextSport(){
        sport = sport.nextSport();
    }

    @Synchronized
    public boolean setWinner(String winner){
        if(this.winner==null){
            this.winner = winner;
            return true;
        }
        return false;
    }

}

class StartLine implements Runnable{
    private final MySignal mySignal;

    StartLine(MySignal mySignal) {
        this.mySignal = mySignal;
    }

    @Override
    public void run() {
        System.out.println(mySignal.getSport().getSportName()+" 比赛开始");
        mySignal.nextSport();
    }
}

定义运动员的动作和干扰者的动作,通过@Async来启动多线程

package demo.cyclicbarrier;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.concurrent.BrokenBarrierException;

@Service
public class TaskService {
    @Async
    public void troublemaker(){
        while(true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }

            //从程序当前运行线程中,随机取出一个线程出来干扰
            Set<Thread> threads = Thread.getAllStackTraces().keySet();
            int size = threads.size();
            int i = 0;
            int index = (int)(Math.random()*size);
            for (Thread thread : threads) {
                if(i!=index){
                    i++;
                    continue;
                }
                thread.interrupt();
                break;
            }
        }
    }

    @Async
    public void swimming(MySignal mySignal, String name){
        SportEnum sportEnum = mySignal.getSport();
        doSport(mySignal, name, sportEnum); //第一场比赛
        
        rest(name);
        sportEnum = mySignal.getSport();
        doSport(mySignal, name, sportEnum); //第二场比赛
        
        rest(name);
        sportEnum = mySignal.getSport();
        doSport(mySignal, name, sportEnum); //第三场比赛
        boolean isWinner = mySignal.setWinner(name);
        if(isWinner==true){
            System.out.println("我是 "+name+", 我是冠军");
        }
    }

    private void rest(String name){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {

        }
        System.out.println(name+" 休息10分钟");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {

        }
    }


    private void doSport(MySignal mySignal, String name, SportEnum sportEnum){
        System.out.println(name +" 到达 "+sportEnum.getSportName()+" 起跑线,等待其他人");
        while(sportEnum==mySignal.getSport()) {
            try {
                if(mySignal.getStartLine().isBroken()){
                    try{
                        Thread.sleep(0);
                    }catch (InterruptedException e){}
                }else{
                    mySignal.getStartLine().await();
                    System.out.println(name + " 开始 " + sportEnum.getSportName());
                }
            } catch (InterruptedException e) {
                System.out.println("我是"+name+",有人干扰我,在起跑线的人都重来");
                mySignal.getStartLine().reset();
            } catch (BrokenBarrierException e) {
                System.out.println("我是"+name+",重新等待 "+sportEnum.getSportName());
            }
        }
    }
}

定义程序入口函数,启动5个运动员和1个干扰者

package demo.cyclicbarrier;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

        final int size = 5;
        TaskService taskService = context.getBean(TaskService.class);
        MySignal mySignal = new MySignal(size);
        for(int i=0;i<size;i++){
            taskService.swimming(mySignal, "man"+i);
        }
        taskService.troublemaker();
        context.close();
    }
}

程序运行结果,可以看到,只有当所有运动员到达起跑线的时候,比赛才会开始

而且,如果在起跑线的运动员,如果被干扰了,会重置起跑线。

man0 到达 游泳 起跑线,等待其他人
man1 到达 游泳 起跑线,等待其他人
man3 到达 游泳 起跑线,等待其他人
man2 到达 游泳 起跑线,等待其他人
man4 到达 游泳 起跑线,等待其他人
游泳 比赛开始
man4 开始 游泳
man0 开始 游泳
man3 开始 游泳
man1 开始 游泳
man2 开始 游泳
man4 休息10分钟
man3 休息10分钟
man4 到达 跑步 起跑线,等待其他人
man2 休息10分钟
man1 休息10分钟
man0 休息10分钟
我是man4,有人干扰我,在起跑线的人都重来
man3 到达 跑步 起跑线,等待其他人
man1 到达 跑步 起跑线,等待其他人
man2 到达 跑步 起跑线,等待其他人
man0 到达 跑步 起跑线,等待其他人
跑步 比赛开始
man4 开始 跑步
man3 开始 跑步
man1 开始 跑步
man2 开始 跑步
man0 开始 跑步
man2 休息10分钟
man0 休息10分钟
man2 到达 骑车 起跑线,等待其他人
man4 休息10分钟
man3 休息10分钟
man1 休息10分钟
man4 到达 骑车 起跑线,等待其他人
man0 到达 骑车 起跑线,等待其他人
我是man2,有人干扰我,在起跑线的人都重来
我是man4,重新等待 骑车
我是man0,重新等待 骑车
我是man2,有人干扰我,在起跑线的人都重来
我是man4,重新等待 骑车
我是man0,重新等待 骑车
man3 到达 骑车 起跑线,等待其他人
man1 到达 骑车 起跑线,等待其他人
骑车 比赛开始
man2 开始 骑车
我是 man2, 我是冠军
man1 开始 骑车
man4 开始 骑车
man0 开始 骑车
man3 开始 骑车

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值