得一面試題,正好用來練手
現有一場田徑比賽,共有8個選手參賽。他們編號分別爲: A,B,C,D,E,F,G,H; 賽事觀察員分別在開始後 20s和30s拍下兩張照片,各個選手的排名情況如下, 20s:F,A,H,G,C,B,E,D;30s:G,H,A,C,F,D,B,E;請你寫出一個程序, 模擬出這些參賽選手在20-30s這10s的內排名變化,每1s顯示一次排名。
主要是考察countDownLatch。
計算的時候clone出快照。避免計算的過程中瞬間值又有變化,導致算出的值不準確。
拍照也就是存一下快照而已。
效果
代碼
package com.gkwind.concurrent;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* 現有一場田徑比賽,共有8個選手參賽。他們編號分別爲: A,B,C,D,E,F,G,H;
* 賽事觀察員分別在開始後 20s和30s拍下兩張照片,各個選手的排名情況如下,
* 20s:F,A,H,G,C,B,E,D;30s:G,H,A,C,F,D,B,E;請你寫出一個程序,
* 模擬出這些參賽選手在20-30s這10s的內排名變化,每1s顯示一次排名。
*
* @Author thewindkee
* @Date 2019/7/18 0018 22:36
*/
public class Athletics {
static final CountDownLatch START_GUN = new CountDownLatch(1);
static final LocalDateTime DATE_TIME = LocalDateTime.now();
static volatile boolean over = false;
static long startTime;
static final int PICTURE_TIME = 20;
static final int PICTURE_TIME2 = 30;
public static void main(String[] args) throws InterruptedException {
int num = 8;
CountDownLatch latch = new CountDownLatch(num);
List<Athlete> athletes = initAthletes(num, latch);
athletes.stream().forEach(athlete -> athlete.start());
System.out.println("裁判等待選手就位");
Computer computer = new Computer(athletes);
computer.start();
latch.await();
System.out.println(DATE_TIME + "時裁判開槍");
startTime = Instant.now().toEpochMilli();
START_GUN.countDown();
computer.join();
computer.showPhotos();
}
private static List<Athlete> initAthletes(int num, CountDownLatch latch) {
return IntStream.range(0, num).mapToObj(i -> new Athlete(String.valueOf((char) ('A' + i)), latch)).collect(Collectors.toList());
}
static class Computer extends Thread {
List<Picture<Athlete>> pictures = new ArrayList<>();
static class Picture<E> {
final List<E> pictureItems;
final long timestamp;
public Picture(List<E> items) {
this.pictureItems = Collections.unmodifiableList(items);
;
this.timestamp = Instant.now().toEpochMilli();
}
public void show() {
pictureItems.stream().forEach(item -> System.out.println(item));
}
@Override
public String toString() {
return "照片拍攝於" + LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()) +
":" + pictureItems +
'}';
}
}
final List<Athlete> athletes;
public Computer(List<Athlete> athletes) {
this.athletes = Collections.unmodifiableList(athletes);
}
public List<Athlete> compute() {
return athletes.stream().map(new Function<Athlete, Athlete>() {
@Override
public Athlete apply(Athlete athlete) {
try {
//拍照,返回運動員的成績快照
return (Athlete) athlete.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
throw new RuntimeException("計算機故障");
}
}).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
}
@Override
public void run() {
try {
System.out.println("電腦已啓動");
START_GUN.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("\n電腦開始工作");
sleepThenTakeFirstPhoto();
while (!over && !isInterrupted()) {
System.out.println();
long currentSecond = (Instant.now().toEpochMilli() - startTime) / 1000 + 1;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
List<Athlete> sortedAthletes = compute();
System.out.print(currentSecond + "秒: ");
sortedAthletes.stream().forEach(athlete -> System.out.print(athlete.getName() + " " + athlete.curLength + " "));
if (currentSecond >= PICTURE_TIME2) {
//第二次拍照
takePhoto(sortedAthletes);
over = true;
break;
}
}
}
private void sleepThenTakeFirstPhoto() {
while (true) {
long currentSecond = (Instant.now().toEpochMilli() - startTime) / 1000 + 1;
try {
TimeUnit.SECONDS.sleep(1);
System.out.print(currentSecond + "秒 ");
} catch (InterruptedException e) {
e.printStackTrace();
}
if (PICTURE_TIME <= currentSecond) {
System.out.println(currentSecond + "秒");
//第一次拍照
List<Athlete> sortedAthletes = compute();
System.out.print(currentSecond + "秒: ");
sortedAthletes.stream().forEach(athlete -> System.out.print(athlete.getName() + " " + athlete.curLength + " "));
takePhoto(sortedAthletes);
break;
}
}
}
private <E> void takePhoto(List<E> datas) {
System.out.print("\ttakePhoto");
pictures.add(new Picture(datas));
}
public void showPhotos() {
System.out.println("\n打印照片");
pictures.stream().forEach(e -> System.out.println(e));
}
}
static class Athlete extends Thread implements Comparable<Athlete>, Cloneable {
final CountDownLatch latch;
final String name;
private volatile int curLength = 0;
public Athlete(String name, CountDownLatch latch) {
super(name);
this.name = name;
this.latch = latch;
}
public void ready() {
System.out.println(this.name + " 進場");
try {
TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("選手" + this.name + " 準備好了,等待發令槍");
latch.countDown();
}
@Override
public void run() {
ready();
try {
START_GUN.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("選手" + this.name + " 起跑\t");
doRun();
}
private void doRun() {
while (!over && !isInterrupted()) {
curLength += ThreadLocalRandom.current().nextInt(8)+1;
//模擬秒速
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public int compareTo(Athlete o) {
return Integer.compare(this.curLength, o.curLength);
}
@Override
protected Object clone() throws CloneNotSupportedException {
Athlete athlete = new Athlete(this.name, null);
athlete.curLength = this.curLength;
return athlete;
}
@Override
public String toString() {
return "Athlete{" +
"name='" + name + '\'' +
", curLength=" + curLength +
'}';
}
}
}