java併發和排序的簡單例子(Runnable+TreeSet)
阿新 • • 發佈:2022-05-30
很多時候併發需要考慮執行緒安全,但也有很多時候和執行緒安全毛關係都沒有,因為併發最大的作用是並行,執行緒安全僅僅是併發的一個子話題。
例如常常會用於併發運算,併發i/o。
下文是一個練習筆記。
執行環境:windows 11,jdk17
1.Pojo--StudentExamScoreSummary
package study.model.school; import java.math.BigDecimal; import java.util.Date; public class StudentExamScoreSummary implements Comparable<StudentExamScoreSummary>{ private Integer id; private Integer studentId; private Integer theYear; private BigDecimal lanScore; private BigDecimal mathScore; private BigDecimal physicalScore; private BigDecimal politicsScore; private BigDecimal philosophyScore; private BigDecimal avgScore;private Date optime; 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 getTheYear() { return theYear; } public void setTheYear(Integer theYear) { this.theYear = theYear; } public BigDecimal getLanScore() { return lanScore; } public void setLanScore(BigDecimal lanScore) { this.lanScore = lanScore; } public BigDecimal getMathScore() { return mathScore; } public void setMathScore(BigDecimal mathScore) { this.mathScore = mathScore; } public BigDecimal getPhysicalScore() { return physicalScore; } public void setPhysicalScore(BigDecimal physicalScore) { this.physicalScore = physicalScore; } 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 getAvgScore() { return avgScore; } public void setAvgScore(BigDecimal avgScore) { this.avgScore = avgScore; } public Date getOptime() { return optime; } public void setOptime(Date optime) { this.optime = optime; } public BigDecimal calAvgScore() { return (this.lanScore.add(this.mathScore).add(this.physicalScore).add(this.politicsScore) .add(this.philosophyScore)).divide(new BigDecimal(5)); } @Override public int compareTo(StudentExamScoreSummary o) { return this.avgScore.compareTo(o.getAvgScore()); } }
2.執行緒和測試程式碼-ConcurrentListRunable
/** * */ package study.base.types.collection.list; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import study.model.school.StudentExamScoreSummary; /** * @author luzhifei * */ public class ConcurrentListRunable implements Runnable { private List<StudentExamScoreSummary> scoreList; DecimalFormat df = new DecimalFormat("#00.0"); ConcurrentHashMap<String, Boolean> flag; public ConcurrentListRunable(List<StudentExamScoreSummary> list, ConcurrentHashMap<String, Boolean> flag) { this.scoreList = list; this.flag = flag; } @Override public void run() { int len = scoreList.size(); String courseName = Thread.currentThread().getName(); for (int i = 0; i < len; i++) { StudentExamScoreSummary item = scoreList.get(i); double rscore = Math.random() * 100; BigDecimal dScore = new BigDecimal(rscore); dScore.setScale(1, RoundingMode.HALF_UP); synchronized (scoreList) { switch (courseName) { case "語文": item.setLanScore(dScore); break; case "數學": item.setMathScore(dScore); break; case "物理": item.setPhysicalScore(dScore); break; case "政治": item.setPoliticsScore(dScore); break; case "哲學": item.setPhilosophyScore(dScore); break; } } //System.out.println(item.getId().toString() + "-[" + courseName + "]" + df.format(dScore)); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized (flag) { flag.put(courseName, true); } } public static void main(String[] args) { int tty=10; String[] courses = new String[] { "數學", "語文", "物理", "政治", "哲學" }; ConcurrentHashMap<String, Boolean> flag = new ConcurrentHashMap<String, Boolean>(5); for (int i = 0; i < courses.length; i++) { flag.put(courses[i], false); } List<StudentExamScoreSummary> sList = new ArrayList<StudentExamScoreSummary>(); for (int i = 0; i < tty; i++) { StudentExamScoreSummary score = new StudentExamScoreSummary(); score.setId(i); score.setStudentId(i); score.setTheYear(2024); sList.add(score); } List<StudentExamScoreSummary> conList = Collections.synchronizedList(sList); ConcurrentListRunable job = new ConcurrentListRunable(conList, flag); List<Thread> jobs = new ArrayList<>(); for (int i = 0; i < courses.length; i++) { Thread t = new Thread(job, courses[i]); jobs.add(t); } for (int i = 0; i < courses.length; i++) { jobs.get(i).start(); } boolean isDone = false; while (isDone == false) { isDone = true; for (int i = 0; i < courses.length; i++) { if (flag.get(courses[i]) == false) { isDone = false; break; } } } DecimalFormat df = new DecimalFormat("#00.0"); for (int i = 0; i < sList.size(); i++) { StudentExamScoreSummary item = sList.get(i); BigDecimal avgScore = item.calAvgScore(); item.setAvgScore(avgScore); System.out.println(item.getStudentId().toString() + " 平均成績:" + df.format(avgScore)); } // 排序 System.out.println("排序------------------------------------------"); TreeSet<StudentExamScoreSummary> ts = new TreeSet<StudentExamScoreSummary>(sList); for (Iterator<StudentExamScoreSummary> iter = ts.iterator(); iter.hasNext();) { StudentExamScoreSummary item=iter.next(); System.out.println(item.getStudentId().toString() + " 平均成績:" + df.format(item.getAvgScore())); } } }
測試結果1:
0 平均成績:48.8
1 平均成績:53.7
2 平均成績:43.8
3 平均成績:56.9
4 平均成績:64.9
5 平均成績:49.9
6 平均成績:51.0
7 平均成績:58.4
8 平均成績:61.3
9 平均成績:48.5
排序------------------------------------------
2 平均成績:43.8
9 平均成績:48.5
0 平均成績:48.8
5 平均成績:49.9
6 平均成績:51.0
1 平均成績:53.7
3 平均成績:56.9
7 平均成績:58.4
8 平均成績:61.3
4 平均成績:64.9
注意事項:
1.StudentExamScoreSummary必須實現Comparable介面,因為測試程式碼使用TreeSet
TreeSet<StudentExamScoreSummary> ts = new TreeSet<StudentExamScoreSummary>(sList);
以上程式碼呼叫的建構函式是:
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
這個建構函式要求入參必須實現Comparable介面(如果引數不是簡單的java型別)。
2.ConcurrentHashMap和Collections.synchronizedList的結果要求手動呼叫sychronized
否則可能會執行緒不安全。
3.程式碼僅僅是為了演示使用5個執行緒各自生成成績,不考慮效率問題。高效且正確的做法應該是分別計算5個不相干的List。
我們需要使用併發list的時候,正常情況下,多數是因為以下理由:
- 對list的操作僅僅佔據了很少的計算資源,執行緒更多的時候是做其它更耗費資源的事情
- 為了實現非同步操作