Concurrent Score Generation and Sorting Using Runnable and TreeSet in Java

Environment: Windows 11, JDK 17

1. POJO – ExamScoreRecord

import java.math.BigDecimal; import java.util.Date;

public class ExamScoreRecord implements Comparable<ExamScoreRecord> { private Integer id; private Integer studentId; private Integer academicYear; private BigDecimal languageScore; private BigDecimal mathScore; private BigDecimal physicsScore; private BigDecimal politicsScore; private BigDecimal philosophyScore; private BigDecimal averageScore; private Date updatedTime;

public Integer getId() {
    return id;
}

public void setId(Integer id) {
    this.id = id;
}

public Integer getStudentId() {
    return studentId;
}

public void setStudentId(Integer studentId) {
    this.studentId = studentId;
}

public Integer getAcademicYear() {
    return academicYear;
}

public void setAcademicYear(Integer academicYear) {
    this.academicYear = academicYear;
}

public BigDecimal getLanguageScore() {
    return languageScore;
}

public void setLanguageScore(BigDecimal languageScore) {
    this.languageScore = languageScore;
}

public BigDecimal getMathScore() {
    return mathScore;
}

public void setMathScore(BigDecimal mathScore) {
    this.mathScore = mathScore;
}

public BigDecimal getPhysicsScore() {
    return physicsScore;
}

public void setPhysicsScore(BigDecimal physicsScore) {
    this.physicsScore = physicsScore;
}

public BigDecimal getPoliticsScore() {
    return politicsScore;
}

public void setPoliticsScore(BigDecimal politicsScore) {
    this.politicsScore = politicsScore;
}

public BigDecimal getPhilosophyScore() {
    return philosophyScore;
}

public void setPhilosophyScore(BigDecimal philosophyScore) {
    this.philosophyScore = philosophyScore;
}

public BigDecimal getAverageScore() {
    return averageScore;
}

public void setAverageScore(BigDecimal averageScore) {
    this.averageScore = averageScore;
}

public Date getUpdatedTime() {
    return updatedTime;
}

public void setUpdatedTime(Date updatedTime) {
    this.updatedTime = updatedTime;
}

public BigDecimal calculateAverage() {
    BigDecimal total = this.languageScore.add(this.mathScore)
            .add(this.physicsScore)
            .add(this.politicsScore)
            .add(this.philosophyScore);
    return total.divide(new BigDecimal(5), 2, BigDecimal.ROUND_HALF_UP);
}

@Override
public int compareTo(ExamScoreRecord other) {
    return this.averageScore.compareTo(other.getAverageScore());
}

}


</div>**2. Runnable and Test Code – `ScorePopulator`**

<div>```
package study.base.types.collection.list;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;

import study.model.school.ExamScoreRecord;

public class ScorePopulator implements Runnable {

    private final List<ExamScoreRecord> records;
    private final Map<String, Boolean> completionFlags;

    public ScorePopulator(List<ExamScoreRecord> records, Map<String, Boolean> flags) {
        this.records = records;
        this.completionFlags = flags;
    }

    @Override
    public void run() {
        int size = records.size();
        String subject = Thread.currentThread().getName();
        for (int i = 0; i < size; i++) {
            ExamScoreRecord record = records.get(i);
            BigDecimal randomScore = new BigDecimal(Math.random() * 100)
                    .setScale(1, RoundingMode.HALF_UP);
            synchronized (records) {
                switch (subject) {
                    case "Language" -> record.setLanguageScore(randomScore);
                    case "Math" -> record.setMathScore(randomScore);
                    case "Physics" -> record.setPhysicsScore(randomScore);
                    case "Politics" -> record.setPoliticsScore(randomScore);
                    case "Philosophy" -> record.setPhilosophyScore(randomScore);
                }
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        completionFlags.put(subject, true);
    }

    public static void main(String[] args) {
        int studentCount = 10;
        String[] subjects = {"Math", "Language", "Physics", "Politics", "Philosophy"};
        Map<String, Boolean> flags = new HashMap<>(subjects.length);
        for (String s : subjects) {
            flags.put(s, false);
        }

        List<ExamScoreRecord> recordList = new ArrayList<>();
        for (int i = 0; i < studentCount; i++) {
            ExamScoreRecord rec = new ExamScoreRecord();
            rec.setId(i);
            rec.setStudentId(i);
            rec.setAcademicYear(2024);
            recordList.add(rec);
        }

        List<ExamScoreRecord> syncedRecords = Collections.synchronizedList(recordList);
        ScorePopulator job = new ScorePopulator(syncedRecords, flags);

        List<Thread> threads = new ArrayList<>();
        for (String subject : subjects) {
            threads.add(new Thread(job, subject));
        }
        threads.forEach(Thread::start);

        // Wait until all threads complete
        boolean allFinished = false;
        while (!allFinished) {
            allFinished = true;
            for (String s : subjects) {
                if (Boolean.FALSE.equals(flags.get(s))) {
                    allFinished = false;
                    break;
                }
            }
        }

        DecimalFormat df = new DecimalFormat("#00.0");
        for (ExamScoreRecord r : recordList) {
            BigDecimal avg = r.calculateAverage();
            r.setAverageScore(avg);
            String output = r.getStudentId() + "-【Language】" + df.format(r.getLanguageScore()) +
                    " 【Math】" + df.format(r.getMathScore()) +
                    " 【Physics】" + df.format(r.getPhysicsScore()) +
                    " 【Politics】" + df.format(r.getPoliticsScore()) +
                    " 【Philosophy】" + df.format(r.getPhilosophyScore()) +
                    "   | Average: " + df.format(avg);
            System.out.println(output);
        }

        // Sort using TreeSet
        System.out.println("Sorted------------------------------------------");
        TreeSet<ExamScoreRecord> sortedSet = new TreeSet<>(recordList);
        for (ExamScoreRecord r : sortedSet) {
            System.out.println(r.getStudentId() + " Average: " + df.format(r.getAverageScore()));
        }
    }
}

1. ExamScoreRecord must implement Comparable because TreeSet relies on natural ordering when constructed from a collection:

TreeSet<ExamScoreRecord> ts = new TreeSet<>(recordList); 
// Calls: public TreeSet(Collection<? extends E> c) { this(); addAll(c); }
// This constructor requires the elements to implement Comparable (unless a custom Comparator is provided).

2. Collections.synchronizedList still requires explicit synchronization

Eventhough the list is wrapped with synchronizedList, compound operations such as setting a score must be synchronized manually to avoid thread interference.

3. The example purely illustrates parallel score generation with five threads; it is not optimized for performance. A more efficient approach would operate on independent lists per subject and merge results later.

Concurrent lists are typically used when:

  • List manipulation represents a small fraction of the overall work, and threads perform more resource-intensive tasks.
  • Asynchronous processing is required.

Tags: java Concurrency TreeSet Runnable Comparable

Posted on Fri, 15 May 2026 18:15:16 +0000 by sullyman