Java 多執行緒 自定義執行緒輔助
之前的文章我介紹了C#版本的多執行緒和自定義執行緒處理器。
接下來我們來看看Java版本的呢
java 的執行緒和C#的執行緒有點區別,java的執行緒沒有是否是後臺執行緒一說,具體原因是java的執行緒是jvm的c++程式碼模擬執行緒,而C#的執行緒也是C++模擬執行緒。但是區別在於C#的執行緒會基於系統的執行緒。
C# 的 Thread.IsBackground;
這裡唯一的區別在於,C#開啟執行緒如果是非後臺執行緒即便是你關閉了程式,如果不是強制退出程序的情況下。執行緒還會繼續執行,知道垃圾回收機制強制回收。如果設定了後臺執行緒標識,關閉程式就直接退出。
java沒有一說。
java執行緒有分組和底層執行緒ID一說。C#沒有。
java的執行緒可以自定義執行緒執行介面 Runnable 或者 重寫執行緒run()方法。
其實這些都是大同小異的,區別性不大。
java執行緒的基礎知識,到處都是,我不在BB
直接開始整體吧~!
1 /** 2 * 執行緒模型 3 * 4 * @author 失足程式設計師 5 * @Blog http://www.cnblogs.com/ty408/ 6 * @mail [email protected] 7 * @phone 13882122019 8 * 9 */ 10 public class ThreadModel extendsThread { 11 12 private static final Logger log = Logger.getLogger(TaskModel.class); 13 private static int threadID = 0; 14 private static final Object SYN_OBJECT = new Object(); 15 private long tid; 16 /** 17 * 任務列表 執行緒安全的任務列表 18 */ 19 protected finalList<TaskModel> taskQueue = Collections.synchronizedList(new LinkedList<TaskModel>()); 20 //false標識刪除執行緒 21 private boolean runing = true; 22 23 public ThreadModel(ThreadGroup group) { 24 this(group, "無名"); 25 } 26 27 public ThreadModel(ThreadGroup group, String name) { 28 super(group, name); 29 synchronized (SYN_OBJECT) { 30 threadID++; 31 tid = threadID; 32 } 33 } 34 35 @Override 36 public long getId() { 37 return this.tid; 38 } 39 40 /** 41 * 增加新的任務 每增加一個新任務,都要喚醒任務佇列 42 * 43 * @param runnable 44 */ 45 public void addTask(TaskModel runnable) { 46 synchronized (taskQueue) { 47 taskQueue.add(runnable); 48 /* 喚醒佇列, 開始執行 */ 49 taskQueue.notify(); 50 } 51 } 52 53 public void setRuning(boolean runing) { 54 this.runing = runing; 55 } 56 57 @Override 58 public void run() { 59 while (runing && ThreadManager.getInstance().isRunning()) { 60 TaskModel r = null; 61 while (taskQueue.isEmpty() && runing && ThreadManager.getInstance().isRunning()) { 62 try { 63 /* 任務佇列為空,則等待有新任務加入從而被喚醒 */ 64 synchronized (taskQueue) { 65 taskQueue.wait(500); 66 } 67 } catch (InterruptedException ie) { 68 log.error(ie); 69 } 70 } 71 synchronized (taskQueue) { 72 /* 取出任務執行 */ 73 if (runing && ThreadManager.getInstance().isRunning()) { 74 r = taskQueue.remove(0); 75 } 76 } 77 if (r != null) { 78 /* 執行任務 */ 79 //r.setSubmitTimeL(); 80 long submitTime = System.currentTimeMillis(); 81 try { 82 r.run(); 83 } catch (Exception e) { 84 log.error("工人<“" + Thread.currentThread().getName() + "”> 執行任務<" + r.getID() + "(“" + r.getName() + "”)> 遇到錯誤: " + e); 85 e.printStackTrace(); 86 } 87 long timeL1 = System.currentTimeMillis() - submitTime; 88 long timeL2 = System.currentTimeMillis() - r.getSubmitTime(); 89 if (timeL1 <= 100L) { 90 log.info("工人<“" + Thread.currentThread().getName() + "”> 完成了任務:" + r.toString() + " 執行耗時:" + timeL1 + " 提交耗時:" + timeL2); 91 } else if (timeL1 <= 1000L) { 92 log.info("工人<“" + Thread.currentThread().getName() + "”> 長時間執行 完成任務:" + r.toString() + " “考慮”任務指令碼邏輯 耗時:" + timeL1 + " 提交耗時:" + timeL2); 93 } else if (timeL1 <= 4000L) { 94 log.info("工人<“" + Thread.currentThread().getName() + "”> 超長時間執行完成 任務:" + r.toString() + " “檢查”任務指令碼邏輯 耗時:" + timeL1 + " 提交耗時:" + timeL2); 95 } else { 96 log.info("工人<“" + Thread.currentThread().getName() + "”> 超長時間執行完成 任務:" + r.toString() + " “考慮是否應該刪除”任務指令碼 耗時:" + timeL1 + " 提交耗時:" + timeL2); 97 } 98 r = null; 99 } 100 } 101 log.error("執行緒結束, 工人<“" + Thread.currentThread().getName() + "”>退出"); 102 } 103 104 @Override 105 public String toString() { 106 return "Thread{" + "tid=" + tid + ",Name=" + this.getName() + '}'; 107 } 108 109 }
這裡建立我們自定義的執行緒模型,有兩點值得注意的是,
protected final List<TaskModel> taskQueue = Collections.synchronizedList(new LinkedList<TaskModel>());
synchronized (taskQueue) { taskQueue.add(runnable); /* 喚醒佇列, 開始執行 */ taskQueue.notify(); } synchronized (taskQueue) { taskQueue.wait(500); }
這裡我們同樣沒有使用Thread.Sleep();對執行緒進行暫停,同樣使用的是 taskQueue.wait();來進行執行緒暫停,這是因為當任務佇列為空的時候,需要暫停執行緒。
當新的任務被放進來的時候,又必須立即開始執行任務。
為了防止執行緒永久暫停,設定的是500毫秒,這樣我們需要關閉程式(ThreadManager.getInstance().isRunning()==false)停止執行緒時候他會自定停止。
輔助鍵值對儲存器
1 /** 2 * 輔助鍵值對儲存 3 * 4 * @author 失足程式設計師 5 * @Blog http://www.cnblogs.com/ty408/ 6 * @mail [email protected] 7 * @phone 13882122019 8 */ 9 public class ObjectAttribute extends HashMap<String, Object> { 10 11 private static final long serialVersionUID = -5320260807959251398L; 12 13 /** 14 * 呼叫此方法 刪除值是需要保證存在key值和value值 否則空指標報錯 15 * 16 * @param <T> 17 * @param key 18 * @param clazz 19 * @return 20 * @deprecated 需要保證存在key值和value值 否則空指標報錯 慎重 21 */ 22 @Deprecated 23 public <T extends Object> T remove(String key, Class<T> clazz) { 24 Object obj = this.remove(key); 25 return (T) obj; 26 } 27 28 /** 29 * 如果未找到也返回 null 30 * 31 * @param key 32 * @return 33 */ 34 public String getStringValue(String key) { 35 if (this.containsKey(key)) { 36 return this.get(key).toString(); 37 } 38 return null; 39 } 40 41 /** 42 * 如果未找到也返回 0 43 * 44 * @param key 45 * @return 46 */ 47 public int getintValue(String key) { 48 if (this.containsKey(key)) { 49 return (int) (this.get(key)); 50 } 51 return 0; 52 } 53 54 /** 55 * 如果未找到也返回 null 56 * 57 * @param key 58 * @return 59 */ 60 public Integer getIntegerValue(String key) { 61 if (this.containsKey(key)) { 62 return (Integer) (this.get(key)); 63 } 64 return null; 65 } 66 67 /** 68 * 如果未找到也返回 0 69 * 70 * @param key 71 * @return 72 */ 73 public long getlongValue(String key) { 74 if (this.containsKey(key)) { 75 return (long) (this.get(key)); 76 } 77 return 0; 78 } 79 80 /** 81 * 如果未找到也返回 null 82 * 83 * @param key 84 * @return 85 */ 86 public Long getLongValue(String key) { 87 if (this.containsKey(key)) { 88 return (Long) (this.get(key)); 89 } 90 return null; 91 } 92 93 /** 94 * 如果未找到也返回 0 95 * 96 * @param key 97 * @return 98 */ 99 public float getfloatValue(String key) { 100 if (this.containsKey(key)) { 101 return (float) (this.get(key)); 102 } 103 return 0; 104 } 105 106 /** 107 * 如果未找到也返回 null 108 * 109 * @param key 110 * @return 111 */ 112 public Float getFloatValue(String key) { 113 if (this.containsKey(key)) { 114 return (Float) (this.get(key)); 115 } 116 return null; 117 } 118 119 /** 120 * 如果未找到也返回 false 121 * 122 * @param key 123 * @return 124 */ 125 public boolean getbooleanValue(String key) { 126 if (this.containsKey(key)) { 127 return (boolean) (this.get(key)); 128 } 129 return false; 130 } 131 132 /** 133 * 如果未找到也返回 null 134 * 135 * @param key 136 * @return 137 */ 138 public Boolean getBooleanValue(String key) { 139 if (this.containsKey(key)) { 140 return (Boolean) (this.get(key)); 141 } 142 return null; 143 } 144 145 @Override 146 public Object clone() { 147 return super.clone(); //To change body of generated methods, choose Tools | Templates. 148 } 149 }View Code
任務執行模型
1 /** 2 * 任務模型 3 * 4 * @author 失足程式設計師 5 * @Blog http://www.cnblogs.com/ty408/ 6 * @mail [email protected] 7 * @phone 13882122019 8 * 9 */ 10 public abstract class TaskModel { 11 12 private static final Logger log = Logger.getLogger(TaskModel.class); 13 14 private long ID; 15 private String Name; 16 //執行時資料 17 private ObjectAttribute runAttribute = new ObjectAttribute(); 18 19 public TaskModel(long ID, String Name) { 20 this.ID = ID; 21 this.Name = Name; 22 this.runAttribute.put("submitTime", System.currentTimeMillis()); 23 } 24 25 public TaskModel() { 26 this(0, "無名"); 27 } 28 29 public long getSubmitTime() { 30 return this.runAttribute.getlongValue("submitTime"); 31 } 32 33 public ObjectAttribute getRunAttribute() { 34 return runAttribute; 35 } 36 37 public void setRunAttribute(ObjectAttribute runAttribute) { 38 this.runAttribute = runAttribute; 39 } 40 41 public long getID() { 42 return ID; 43 } 44 45 public String getName() { 46 return Name; 47 } 48 49 public abstract void run(); 50 51 @Override 52 public String toString() { 53 return "TaskModel{" + "ID=" + ID + ", Name=" + Name + ", runAttribute=" + runAttribute + '}'; 54 } 55 56 }
接下來我們測試一下
1 ThreadModel threadModel = new ThreadModel(new ThreadGroup("Test"), "Test"); 2 threadModel.start(); 3 threadModel.addTask(new TaskModel() { 4 5 @Override 6 public void run() { 7 System.out.println("TaskModel Test"); 8 } 9 });
執行結果
TaskModel Test
[04-24 17:31:26:0223:INFO : sz.network.threadpool.TaskModel:97 行] -> 工人<“Test”> 完成了任務:TaskModel{ID=0, Name=無名, runAttribute={submitTime=1429867886219}} 執行耗時:0 提交耗時:4
我們看到居然有提交耗時,,別奇怪,,因為在某些情況下,執行緒的從暫停狀態到喚醒狀態需要消耗時間的,系統不可能有那麼多空閒資源,收到你的命令馬上就放棄一切事情執行你的命令。
我們除錯執行時可以看見當前程式所有執行緒,以及分組情況(我使用的是NetBeans IDE 8.0.2 開發工具)
接下來我們來構建一下。後臺執行緒池,
1 /** 2 * 後臺執行緒池 3 * 4 * @author 失足程式設計師 5 * @Blog http://www.cnblogs.com/ty408/ 6 * @mail [email protected] 7 * @phone 13882122019 8 */ 9 class BackThread { 10 11 private static final Logger log = Logger.getLogger(BackThread.class); 12 13 private final ThreadGroup threadGroup = new ThreadGroup(ThreadManager.getGlobeThreadGroup(), "後臺執行器"); 14 15 /* 任務列表 */ 16 private final List<TaskModel> taskQueue = Collections.synchronizedList(new LinkedList<TaskModel>()); 17 private final BackThreadRunnable backThreadRunnable = new BackThreadRunnable(); 18 19 public BackThread() { 20 int threadcountI = 10; 21 for (int i = 1; i <= threadcountI; i++) { 22 Thread thread = new Thread(threadGroup, backThreadRunnable, "後臺執行緒-" + i); 23 thread.start(); 24 } 25 log.info("---初始化後臺執行緒池--執行緒數量:" + threadcountI + "------------"); 26 } 27 28 /** 29 * 增加新的任務 每增加一個新任務,都要喚醒任務佇列 30 * 31 * @param newTask 32 */ 33 public void addTask(TaskModel newTask) { 34 synchronized (taskQueue) { 35 taskQueue.add(newTask); 36 /* 喚醒佇列, 開始執行 */ 37 taskQueue.notify(); 38 } 39 } 40 41 final class BackThreadRunnable implements Runnable { 42 43 /** 44 * 迴圈執行任務 45 */ 46 @Override 47 public void run() { 48 while (ThreadManager.getInstance().isRunning()) { 49 TaskModel r = null; 50 synchronized (taskQueue) { 51 while (taskQueue.isEmpty() && ThreadManager.getInstance().isRunning()) { 52 try { 53 /* 任務佇列為空,則等待有新任務加入從而被喚醒 */ 54 taskQueue.wait(500); 55 } catch (InterruptedException ie) { 56 log.error(ie); 57 } 58 } 59 /* 取出任務執行 */ 60 if (ThreadManager.getInstance().isRunning()) { 61 r = taskQueue.remove(0); 62 } 63 } 64 if (r != null) { 65 /* 執行任務 */ 66 //r.setSubmitTimeL(); 67 long submitTime = System.currentTimeMillis(); 68 try { 69 r.run(); 70 } catch (Exception e) { 71 e.printStackTrace(); 72 log.error("工人<“" + Thread.currentThread().getName() + "”> 執行任務<" + r.getID() + "(“" + r.getName() + "”)> 遇到錯誤: " + e); 73 } 74 long timeL1 = System.currentTimeMillis() - submitTime; 75 long timeL2 = System.currentTimeMillis() - r.getSubmitTime(); 76 if (timeL1 <= 100L) { 77 log.info("工人<“" + Thread.currentThread().getName() + "”> 完成了任務:" + r.toString() + " 執行耗時:" + timeL1 + " 提交耗時:" + timeL2); 78 } else if (timeL1 <= 1000L) { 79 log.info("工人<“" + Thread.currentThread().getName() + "”> 長時間執行 完成任務:" + r.toString() + " “考慮”任務指令碼邏輯 耗時:" + timeL1 + " 提交耗時:" + timeL2); 80 } else if (timeL1 <= 4000L) { 81 log.info("工人<“" + Thread.currentThread().getName() + "”> 超長時間執行完成 任務:" + r.toString() + " “檢查”任務指令碼邏輯 耗時:" + timeL1 + " 提交耗時:" + timeL2); 82 } else { 83 log.info("工人<“" + Thread.currentThread().getName() + "”> 超長時間執行完成 任務:" + r.toString() + " “考慮是否應該刪除”任務指令碼 耗時:" + timeL1 + " 提交耗時:" + timeL2); 84 } 85 r = null; 86 } 87 } 88 log.error("執行緒結束, 工人<“" + Thread.currentThread().getName() + "”>退出"); 89 } 90 } 91 }
以及定時器執行緒處理器
1 /** 2 * 定時器執行緒 3 * 4 * @author 失足程式設計師 5 * @Blog http://www.cnblogs.com/ty408/ 6 * @mail [email protected] 7 * @phone 13882122019 8 */ 9 class TimerThread extends ThreadModel { 10 11 private static final Logger log = Logger.getLogger(TimerThread.class); 12 13 public TimerThread() { 14 super(ThreadManager.getGlobeThreadGroup(), "全域性定時器執行緒"); 15 this.start(); 16 } 17 18 @Override 19 public void run() { 20 while (ThreadManager.getInstance().isRunning()) { 21 while (ThreadManager.getInstance().isRunning() && taskQueue.isEmpty()) { 22 try { 23 /* 任務佇列為空,則等待有新任務加入從而被喚醒 */ 24 synchronized (taskQueue) { 25 taskQueue.wait(200); 26 } 27 } catch (InterruptedException ie) { 28 } 29 } 30 ArrayList<TaskModel> taskModels; 31 synchronized (taskQueue) { 32 //佇列不為空的情況下 取出佇列定時器任務 33 taskModels = new