Toggle Theme Editor
Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate Charcoal

Task And / Or Thread

Discussion in 'Java Update' started by Joe, 26/9/19.

  1. Joe

    Joe Thành viên VIP

    In my tutorial Java Essentials For Newbies And Developers I've briefly talked about the difference between Task and Thread. Today I discuss more about this issue.

    As said, Task and Thread are independent processes which run along with their "owner". Thread is a lightweight of Task. Meaning: Thread rarely has its own working environment. Mostly thread works within its parents environment where data are shared between parents and threads. Because of sharing data data must be synchronized between parents and threads. Otherwise the data consistency would be unpredictable. If the parents data are declared volatile their actual values are taken by JVM directly from memory without "optimizing them" (i.e. using the copied or referenced value for processing). Example:
    PHP:
    // source: https://dzone.com/articles/java-volatile-keyword-0
    // modified: change LOGGER to println and makes the volatile usage clearer
    public class VolatileTest {
        private static 
    int MY_INT 0;
        public static 
    void main(String[] args) {
            new 
    ChangeListener().start();
            new 
    ChangeMaker().start();
        }
        static class 
    ChangeListener extends Thread {
            @
    Override
            
    public void run() {
                
    int local_value MY_INT;
                while ( 
    local_value 5){
                    if( 
    local_value != MY_INT){
                        
    System.out.println("local_value:"+local_value+"-Got Change for MY_INT :"MY_INT);
                        
    local_value MY_INT;
                    }
                }
            }
        }
        static class 
    ChangeMaker extends Thread{
            @
    Override
            
    public void run() {
     
                
    int local_value MY_INT;
                while (
    MY_INT 5){
                    
    MY_INT = ++local_value;
                    
    System.out.println("local_value:"+local_value+"->Incrementing MY_INT to "+MY_INT);
                    try {
                        
    Thread.sleep(100);
                    } catch (
    Exception e) {  }
                }
            }
        }
    }
    The output with volatile declaration:
    PHP:
    C:\JFX\TestII>java VolatileTest
    local_value
    :1->Incrementing MY_INT to 1
    local_value
    :0->Got Change for MY_INT :1
    local_value
    :2->Incrementing MY_INT to 2
    local_value
    :1->Got Change for MY_INT :2
    local_value
    :3->Incrementing MY_INT to 3
    local_value
    :2->Got Change for MY_INT :3
    local_value
    :3->Got Change for MY_INT :4
    local_value
    :4->Incrementing MY_INT to 4
    local_value
    :4->Got Change for MY_INT :5
    local_value
    :5->Incrementing MY_INT to 5
     
    C
    :\JFX\TestII>
    As you see, Got and Incrementing have the same value. If the keyword volatile is removed the ChangeListener thread will loop forever because JVM tries to optimize the data with the "most actual" value and causes the troubles (here: local_value is assigned to the copied value of MY_INT which is now 1 and that makes them becoming equal).

    Output without volatile
    PHP:
    C:\JFX\TestII>java VolatileTest
    local_value
    :1->Incrementing MY_INT to 1
    local_value
    :0->Got Change for MY_INT :1
    local_value
    :2->Incrementing MY_INT to 2
    local_value
    :3->Incrementing MY_INT to 3
    local_value
    :4->Incrementing MY_INT to 4
    local_value
    :5->Incrementing MY_INT to 5
    Volatile data are always accessed by JVM from the main memory when they are referenced. Same to synchronized data. The only difference between volatile and synchronization is that volatile data could be inconsistent because they can be interfered by other threads, but synchronized data are kept away from such an interfering and therefore consistent. If you have to work with sub-processes and want to monitor their status you have the choice between Thread and Task.

    THREAD
    Thread is a stateless process. Meaning: thread won't return anything after termination. To monitor a thread, especially when it is run by ExecutiveService, the parents process has to devise some "state" for its thread. The devised "state" should be independent from other threads and can be only changed by the thread itself and accessed by its parents.

    TASK
    Task is on the other hand a stateful process. A task always returns something when it terminates. Object Void is used if it won't return anything (JAVA: null). Example:
    PHP:
      class VoidTask implements Callable<Void> {
        public 
    vTask(int i) {
          ...
        }
        public 
    Void call() {
          ...
          return 
    null// for Void
        
    }
        private 
    int i;
      }
     
    The parents can verify the returned value at any time for the processing status of the task using the API Future<V> where V is the generic form for any Java Object (e.g. int -> Integer, etc.).

    SYNCHRONIZATION
    If parents data are used in both cases (Thread and Task) the data have to be synchronized. The keyword synchronized is here for the usage.

    Keyword this is for primitives or an expression block. Example: local: "int cnt", parents: "int counter"
    PHP:
    int cnt;
    synchronized(this) {
      
    cnt counter++;
    }
    Object ArrayList<String> of parents
    PHP:
    synchronized(this) {
      
    aList.add("Joe");
    }
    // or
    synchronized(aList) {
      
    aList.add("Joe");
    }
    The following example shows you how Tasks and Threads work within Java ExecutiveService.
    PHP:
    import java.util.concurrent.*;
    // Joe Nartca (C)
    public class TaskAndThread {
      public 
    TaskAndThread(int nthrows Exception {
        
    ExecutorService pool Executors.newCachedThreadPool( );
        
    = new long[n];
        
    = new String[n];
        
    th = new boolean[n];
        for (
    int i 0n; ++ith[i] = false;
        
    System.out.println("-----------Working with THREAD-----------");
        for (
    int i 0n; ++i) {
          
    t[i] = System.nanoTime();
          
    pool.submit(new aThread(i));
        }
        
    int i 0;
        while (
    n) { // check for Thread termination
          
    for (int l 0n; ++l) if (th[l]) {
            
    System.out.print(s[l]);
            
    th[l] = false;
            ++
    i;
          }
        }
        
    System.out.println("-----------Working with TASK-----------");
        
    java.util.ArrayList<Future<Boolean>> = new java.util.ArrayList<Future<Boolean>>();
        for (
    0n; ++i) {
          
    t[i] = System.nanoTime();
          
    a.add(pool.submit(new aTask(i)));
        }
        
    0;  // check for Task termination
        
    for (Future<Booleana) {
          if (
    f.get()) {
            
    System.out.print(s[i++]);
          }
        }
        
    pool.shutdown( );
      }
      public static 
    void main(String... argvthrows Exception {
        new 
    TaskAndThread(argv.length 0Integer.parseInt(argv[0]):10);
      }
      
    //------------------------ Task ------------------------
      
    private class aTask implements Callable<Boolean> {
        public 
    aTask(int i) {
          
    this.i;
        }
        public 
    Boolean call() {
          
    //
          // your codes
          //
          
    int cnt;
          
    synchronized(this) {
            
    cnt counter++;
          }
          
    s[i] = String.format("Task_%d terminates. Time:%.3f milliSec. Counter:%d\n",
                               
    i,((double)System.nanoTime( )-t[i])/1000000cnt);
          return 
    true;
        }
        private 
    int i;
      }
      
    //------------------------ Thread ------------------------
      
    private class aThread implements Runnable {
        public 
    aThread(int i) {
          
    this.i;
        }
        public 
    void run() {
          
    //
          // your codes
          //
          
    int cnt;
          
    synchronized(this) {
            
    cnt counter++;
          }
          
    s[i] = String.format("Thread_%d terminates. Time:%.3f milliSec. Counter:%d\n",
                               
    i,((double)System.nanoTime( )-t[i])/1000000cnt);
          
    th[i] = true// saying "finished"
        
    }
        private 
    int i;
      }
      private 
    long[] t;
      private 
    String[] s;
      private 
    int counter;
      private 
    boolean th[];
    }
    The output
    PHP:
    C:\JFX\TestII>java TaskAndThread
    -----------Working with THREAD-----------
    Thread_9 terminatesTime:0,531 milliSecCounter:8
    Thread_0 terminates
    Time:16,063 milliSecCounter:0
    Thread_1 terminates
    Time:0,760 milliSecCounter:1
    Thread_2 terminates
    Time:1,184 milliSecCounter:4
    Thread_3 terminates
    Time:0,896 milliSecCounter:2
    Thread_4 terminates
    Time:0,782 milliSecCounter:3
    Thread_5 terminates
    Time:1,078 milliSecCounter:6
    Thread_6 terminates
    Time:0,856 milliSecCounter:5
    Thread_7 terminates
    Time:1,136 milliSecCounter:9
    Thread_8 terminates
    Time:0,709 milliSecCounter:7
    -----------Working with TASK-----------
    Task_0 terminatesTime:2,162 milliSecCounter:0
    Task_1 terminates
    Time:0,029 milliSecCounter:1
    Task_2 terminates
    Time:0,023 milliSecCounter:2
    Task_3 terminates
    Time:0,023 milliSecCounter:3
    Task_4 terminates
    Time:0,022 milliSecCounter:4
    Task_5 terminates
    Time:0,023 milliSecCounter:5
    Task_6 terminates
    Time:0,021 milliSecCounter:6
    Task_7 terminates
    Time:0,021 milliSecCounter:7
    Task_8 terminates
    Time:0,024 milliSecCounter:8
    Task_9 terminates
    Time:0,025 milliSecCounter:9
     
    C
    :\JFX\TestII>
    As you see, task is in this case faster than thread. The reason is simple: Thread has to work with a global variable (here: boolean th[ ]) while Task simply return a (local) value.

    The API Executors gives a variety of Service-choices. The newWorkStealingPool is purposed for parallelism. Precondition is that your computer supports parallelism. In case of parallelism, especially with parallelStream(), you should be very aware about "stateless" or "stateful"of the processes. Otherwise you could get wrong results. A stateless process is independent from any external event such as modification, waiting for a result, etc. In this case Parallelism is ideal. More about Parallelism: HERE or THIS.
     
    Last edited: 28/9/19

Chia sẻ trang này

Loading...