官术网_书友最值得收藏!

Updating the process list

If the application started and showed a list of processes, but never updated that list, it wouldn't be very useful at all. What we then need is a way to update the list periodically, and for that, we'll use a Thread.

As you may or may not know, a Thread is roughly a means to run a task in the background (the Javadoc describes it as a thread of execution in a program). A system can be single or multithreaded, depending on the needs and runtime environment of the system. And multithreaded programming is hard to get right. Luckily, our use case here is fairly simple, but we must still exercise caution, or we'll see some really unexpected behavior.

Ordinarily, the advice you would get when creating a Thread is to implement a Runnable interface, which you will then pass to the thread's constructor, and that's very good advice, as it makes your class hierarchy much more flexible, since you're not tied to a concrete base class (Runnable is an interface). In our case, however, we have a relatively simple system that has little to gain from that approach, so we'll extend Thread directly and simplify our code a little as well as encapsulating our desired behavior. Let's take a look at our new class:

    private class ProcessListUpdater extends Thread { 
      private volatile boolean running = true; 
 
      public ProcessListRunnable() { 
        super(); 
        setDaemon(true); 
      } 
 
      public void shutdown() { 
        running = false; 
      } 
 
      @Override 
      public void run() { 
        while (running) { 
          updateList(); 
          try { 
            Thread.sleep(5000); 
          } catch (InterruptedException e) { 
              // Ignored 
            } 
        } 
      }  
 
      public synchronized void updateList() { 
        processList.setAll(ProcessHandle.allProcesses() 
          .collect(Collectors.toList())); 
        processView.sort(); 
      } 
    } 

We have a pretty basic class, which we've given a reasonable and meaningful name that extends Thread. In the constructor, note that we call setDaemon(true). This will allow our application to exit as expected and not block, waiting for the thread to terminate. We've also defined a shutdown() method, which we'll use from our application to stop the thread.

The Thread class does have various state control methods, such as stop(), suspend(), resume(), and more, but these have all been deprecated as they are considered inherently unsafe. Search for the article, Why are Thread.stop, Thread.suspend, and Thread.resume deprecated? If you would like more details; however, the suggested best practice now is to use a control flag, like we've done with running, to signal to the Thread class that it needs to clean up and shut down.

Finally, we have the heart of our Thread class, run(), which loops infinitely (or until running becomes false), sleeping for five seconds after performing its work. The actual work is done in updateList(), which builds the list of processes, updates ObservableList we discussed earlier, and then instructs TableView to re-sort itself, based on the user's sort selection, if any. This is a public method, allowing us to call this at need, as we did in killProcessHandler(). That leaves us with the following block of code to set it up:

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 
      processListUpdater = new ProcessListUpdater(); 
      processListUpdater.start(); 
      // ... 
    } 

The following code will shut it down, which we've already seen in closeHandler():

    processListUpdater.shutdown(); 

The eagle-eyed will notice that updateList() has the synchronized keyword on it. This is to prevent any sort of race condition that might be caused by calling this method from multiple threads. Imagine the scenario where the user decides to kill a process and clicks on OK on the confirmation dialog at the exact moment the thread wakes up (this type of thing happens more often than you might think). We could conceivably have two threads calling updateList() at the same time, resulting in the first thread hitting processView.sort() just as the second is hitting processList.setAll(). What happens when sort() is called while another thread is rebuilding the list? It's hard to say for sure, but it could be catastrophic, so we want to disallow that. The synchronized keyword instructs the JVM to allow only one thread to execute the method at a time, causing all others to queue up, waiting their turn (note that their execution order is non-deterministic, so you can't base any expectations on the order in which threads get to run a synchronized method). This avoids the potential for a race condition, and ensures that our program doesn't crash.

While appropriate here, care must be taken with synchronized methods, as acquiring and releasing the locks can be expensive (though much less so with modern JVMs) and, more importantly, it forces threads to run sequentially when they hit this method call, which can cause a very undesirable lag in the application, especially in GUI applications. Keep that in mind when writing your own multithreaded applications.

主站蜘蛛池模板: 武穴市| 龙井市| 乌拉特前旗| 兰州市| 米林县| 颍上县| 个旧市| 弋阳县| 北京市| 泰顺县| 那曲县| 遂昌县| 万源市| 武乡县| 秭归县| 太谷县| 揭西县| 台南市| 贵溪市| 浪卡子县| 根河市| 宽甸| 嘉荫县| 石首市| 潮州市| 佛山市| 焦作市| 柘荣县| 临沭县| 新蔡县| 孝感市| 乌兰浩特市| 常熟市| 永年县| 东乡族自治县| 龙胜| 中阳县| 阿勒泰市| 宁夏| 灵寿县| 南投县|