java thread programming 1.4 – Implementing Runnable Versus Extending Thread
來源:互聯網
上載者:User
Implementing Runnable Versus Extending Thread Rather than inherit from Thread, a class can implement the interface java.lang.Runnable to allow a thread to be run within it. Runnable specifies that only one method be implemented:public void run() This is the same method signature that run() has in Thread. In fact, Thread also implements Runnable! Note that run() does not take any parameters, does not return anything, and does not declare that it throws any exceptions. 1、一個類實現Runnable介面就允許建立新線程在其內部運行run()方法中的statment2、Thread也是實現Runnable介面3、實現Runnable介面唯一要做的事情就是實現run()方法,注意此方法,無參數,無傳回值 The Thread class has four constructors that take a Runnable object as a parameter:public Thread(Runnable target)public Thread(Runnable target, String name)public Thread(ThreadGroup group, Runnable target)public Thread(ThreadGroup group, Runnable target, String name) Any instance of a class that implements the Runnable interface may be passed as the target to one of these constructors. When the Thread instance’s start() method is invoked, start() will start the new thread in the run() method of target rather than in Thread’s run() method. The Runnable to be used may be specified only at the time of a Thread’s construction; the Thread holds a reference to it in a private member variable. 實現了Runnable介面的類,有以上幾種方法變為線程,然後調用Thread的start()方法執行類中的run()方法
SecondCounter.java
/* * Created on 2005-7-7 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ package org.tju.msnrl.jonathan.thread.chapter1; import javax.swing.*;import java.awt.*;import java.text.*; /** * @author Jonathan Q. Bo * * TODO To change the template for this generated type comment go to * Window - Preferences - Java - Code Style - Code Templates */ public class SecondCounter extends JComponent implements Runnable { private
volatile boolean keepRunning; private
volatile int arcLen; private
volatile String timeMsg; private Font paintFont; public SecondCounter(){ this.keepRunning = true; this.paintFont = new Font("SansSerif",Font.BOLD,14); this.timeMsg = "never start!"; } public void runClock(){ DecimalFormat df = new DecimalFormat("0.000"); long normalSleepTime = 100; long nextSleepTime = normalSleepTime; int counter = 0; long startTime = System.currentTimeMillis(); this.keepRunning = true; while(this.keepRunning){ try{ Thread.sleep(nextSleepTime); }catch(InterruptedException e){ System.out.println("Interrupted Exception " + e.getMessage()); } /*improve the accuracy*/ counter++; double counterSec = counter / 10.0; double elapseSec = (System.currentTimeMillis() - startTime)/1000.0; double diffSec = counterSec - elapseSec; nextSleepTime = normalSleepTime + (long)(diffSec*1000); if(nextSleepTime < 0) nextSleepTime = 0; this.timeMsg = "passed time should be " + counterSec + ", in fact be " + elapseSec + ",the diff is " + diffSec; this.arcLen = (((int)counterSec)%60) * 360 / 60;//角度 repaint(); } } public void stopClock(){ this.keepRunning = false; } /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { this.runClock(); } public void paint(Graphics g){ g.setColor(Color.BLACK); g.setFont(this.paintFont); g.drawString(this.timeMsg,0,15); g.fillOval(0,20,100,100); g.setColor(Color.WHITE); g.fillOval(3,23,94,94); g.setColor(Color.BLUE); g.fillArc(2,22,96,96,90,this.arcLen); }}
SecondCounterMain.java /* * Created on 2005-7-7 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */package org.tju.msnrl.jonathan.thread.chapter1; import javax.swing.*;import javax.swing.border.*;import java.awt.*;import java.awt.event.*; /** * @author Administrator * * TODO To change the template for this generated type comment go to * Window - Preferences - Java - Code Style - Code Templates */public class SecondCounterMain extends JPanel { private JButton startB; private JButton stopB; private SecondCounter sc; public SecondCounterMain(){ sc = new SecondCounter(); startB = new JButton("start"); stopB = new JButton("stop"); startB.addActionListener( new ActionListener(){ public void actionPerformed(ActionEvent e){ startB.setEnabled(false); Thread countThread = new Thread(sc,"SecondCounter"); countThread.start(); stopB.setEnabled(true); stopB.requestFocus(); } } ); stopB.addActionListener( new ActionListener(){ public void actionPerformed(ActionEvent e){ stopB.setEnabled(false); sc.stopClock(); startB.setEnabled(true); startB.requestFocus(); } } ); JPanel innerButtonP = new JPanel(); innerButtonP.setLayout(new GridLayout(0,1,0,3)); innerButtonP.add(startB); innerButtonP.add(stopB); JPanel buttonP = new JPanel(); buttonP.setLayout(new BorderLayout()); buttonP.add(innerButtonP,BorderLayout.NORTH); this.setLayout(new BorderLayout(10,10)); this.setBorder(new EmptyBorder(20,20,20,20)); this.add(buttonP,BorderLayout.WEST); this.add(sc,BorderLayout.CENTER); } public static void main(String[] args) { SecondCounterMain scm = new SecondCounterMain(); JFrame f = new JFrame("second counter"); f.setContentPane(scm); f.setSize(620,200); f.setVisible(true); f.addWindowListener( new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } } ); }} On lines 6, 8, and 9 of Listing 4.3, the modifier
volatile is included for some of the member variables. By indicating that a member variable is volatile, you inform the JavaVM that its value might be changed by one thread while being used by another. In this case, one thread is checking keepRunning, and another thread will change its value to false some time after the timer is started. Under certain circumstances, if the variable was not marked as volatile, the while loop would not see the new value and would run the timer forever. This is an important detail often overlooked (even by experienced developers) and is discussed in detail in Chapter 7, “Concurrent Access to Objects and Variables.” 修飾符volatile含義:一個變數的值可以被一個線程修改,同時被其他線程使用,就像上面的例子,java虛擬機器建立的awt訊息響應處理線程可能修改keeprunning變數,而同時背景工作執行緒一直在檢測此變數的值是否為真。如果不使用此修飾符,可能修改後的值永遠不會被背景工作執行緒也就是本程式中的計時器檢測到。這是一個很重要的細節。 Although not many statements exist in the while loop, it has been shown that over time, they cause the loop to run significantly more slowly than desired. To improve the accuracy, the sleep time should be varied based on the current system clock time. The final version of SecondCounter is simply called SecondCounter; its code appears in Listing 4.7. A new local variable named nextSleepTime is used to vary the number of milliseconds to sleep each time through the loop (lines 24 and 32). The nextSleepTime value is recalculated based on the difference between the counter seconds and the system clock seconds (lines 42–45). If this value happens to be less than zero, zero is used instead because it’s impossible to sleep for a negative amount of time (lines 47–49). Thread.sleep()並不是很準確的讓線程停止一定時間,如何增強計時器的即時性?上面的例子給了一個有效解決方案。檢測時間差,動態改變線程休息時間。 Here are a few other lessons learned: Do not use the event handling thread to perform long-running tasks; it should be allowed to return to the business of handling events relatively quickly. For the long tasks, use a worker thread. This is critical if a Stop or Cancel Request button exists and could be pressed before the original task completes. Proper use of the volatile keyword is not trivial and is necessary in many cases to guarantee desired code execution behavior when two or more threads access the same member variable. Chapter 7 explains volatile in detail. The amount of time it takes to execute even a few statements is not predictable. This time can become significant if the statements are executed over and over in a loop. When accuracy is important, you should check the system clock to see how much real time actually elapsed and then make adjustments accordingly. 通過學習本章我們應該知道:1、不要在事件處理線程中作長時間的作業,而應該使用背景工作執行緒,最好有stop/cancel之類的按鈕可以在任務完成前可以被執行2、要正確使用volatile修飾符,這是保證預期程式碼為的必要手段,尤其是在多線程訪問同一變數的時候3、還有就是執行任務的時間是不確定的,如果程式要求很高的精確度,就應該檢測理論和現實的時間差,作相應的調整