Details the methods of thread synchronization and timing in Java programming

  • 2020-04-01 04:33:32
  • OfStack

Use wait() and notify() to implement inter-thread collaboration
1. The wait () and notify ()/notifyAll ()
The lock is not released when sleep() and yield() are called, and the call wait() releases the lock. This allows another task (thread) to acquire the lock of the current object and enter its synchronized method. You can resume execution from wait() by notifying ()/notifyAll(), or when the time expires.
Wait (), notify(), and notifyAll() can only be called in synchronous control methods or synchronous blocks. If in a synchronized method call these methods, raise IllegalMonitorStateException anomalies at run time.
2. Simulate the wake up of a single thread to multiple threads
Simulate collaboration between threads. The Game class has two synchronized methods prepare() and go(). The flag bit start is used to determine whether the current thread needs to wait(). An instance of the Game class starts all instances of the Athele class in the wait() state, and after a while, changes the flag bit and notifyAll() all Athele threads in the wait state.
Game. The Java


package concurrency;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

class Athlete implements Runnable {
  private final int id;
  private Game game;

  public Athlete(int id, Game game) {
   this.id = id;
   this.game = game;
  }

  public boolean equals(Object o) {
   if (!(o instanceof Athlete))
    return false;
   Athlete athlete = (Athlete) o;
   return id == athlete.id;
  }

  public String toString() {
   return "Athlete<" + id + ">";
  }

  public int hashCode() {
   return new Integer(id).hashCode();
  }

  public void run() {
   try {
    game.prepare(this);
   } catch (InterruptedException e) {
    System.out.println(this + " quit the game");
   }
  }
 }

public class Game implements Runnable {
  private Set<Athlete> players = new HashSet<Athlete>();
  private boolean start = false;

  public void addPlayer(Athlete one) {
   players.add(one);
  }

  public void removePlayer(Athlete one) {
   players.remove(one);
  }

  public Collection<Athlete> getPlayers() {
   return Collections.unmodifiableSet(players);
  }

  public void prepare(Athlete athlete) throws InterruptedException {
   System.out.println(athlete + " ready!");
   synchronized (this) {
    while (!start)
    wait();
    if (start)
     System.out.println(athlete + " go!");
   }
  }

  public synchronized void go() {
   notifyAll();
  }
  
  public void ready() {
   Iterator<Athlete> iter = getPlayers().iterator();
   while (iter.hasNext())
    new Thread(iter.next()).start();
  }

  public void run() {
   start = false;
   System.out.println("Ready......");
   System.out.println("Ready......");
   System.out.println("Ready......");
   ready();
   start = true;
   System.out.println("Go!");
   go();
  }

  public static void main(String[] args) {
   Game game = new Game();
   for (int i = 0; i < 10; i++)
    game.addPlayer(new Athlete(i, game));
   new Thread(game).start();
  }
}

Results:


Ready......
Ready......
Ready......
Athlete<0> ready!
Athlete<1> ready!
Athlete<2> ready!
Athlete<3> ready!
Athlete<4> ready!
Athlete<5> ready!
Athlete<6> ready!
Athlete<7> ready!
Athlete<8> ready!
Athlete<9> ready!
Go!
Athlete<9> go!
Athlete<8> go!
Athlete<7> go!
Athlete<6> go!
Athlete<5> go!
Athlete<4> go!
Athlete<3> go!
Athlete<2> go!
Athlete<1> go!
Athlete<0> go!

3. Simulate the busy waiting process
An instance of the MyObject class is the observed, which notifies an instance of the Monitor class when the observed event occurs (by changing a flag bit). An instance of this Monitor class is constantly checking for changes in flag bits by busy waiting.
BusyWaiting. Java


import java.util.concurrent.TimeUnit;

class MyObject implements Runnable {
  private Monitor monitor;

  public MyObject(Monitor monitor) {
   this.monitor = monitor;
  }

  public void run() {
   try {
    TimeUnit.SECONDS.sleep(3);
    System.out.println("i'm going.");
    monitor.gotMessage();
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
}

class Monitor implements Runnable {
  private volatile boolean go = false;

  public void gotMessage() throws InterruptedException {
   go = true;
  }

  public void watching() {
   while (go == false)
    ;
   System.out.println("He has gone.");
  }

  public void run() {
   watching();
  }
}

public class BusyWaiting {
  public static void main(String[] args) {
   Monitor monitor = new Monitor();
   MyObject o = new MyObject(monitor);
   new Thread(o).start();
   new Thread(monitor).start();
  }
}

Results:


i'm going.
He has gone.

4. Rewrite the above example using wait() and notify()
The following example replaces the busy wait mechanism with wait(), which notifies the current Monitor class thread when a notification message is received.
Wait. The Java


package concurrency.wait;

import java.util.concurrent.TimeUnit;

class MyObject implements Runnable {
  private Monitor monitor;

  public MyObject(Monitor monitor) {
   this.monitor = monitor;
  }

Timed start thread
There are two ways to start a thread after a specified time. One is through the Java. Util. Concurrent. DelayQueue implementation; Secondly, through the Java. Util. Concurrent. ScheduledThreadPoolExecutor implementation.
1. Java. Util. Concurrent. DelayQueue
The DelayQueue class is an unbounded blocking queue from which elements can be extracted only when the delay expires. It accepts as an element an instance that implements the Delayed interface.
< < Interface> > Of Java


package java.util.concurrent;
import java.util.*;
public interface Delayed extends Comparable<Delayed> {
  long getDelay(TimeUnit unit);
}

GetDelay () returns the remaining delay time associated with this object, expressed in given units of time. The implementation of this interface must define a compareTo method that provides a sort consistent with the interface's getDelay method.

The head of the DelayQueue queue is the longest Delayed element to be held after the delay expires. Expiration occurs when the getDelay(timeunit.nanoseconds) method of an element returns a value less than or equal to 0.
2. Design a queue with time delay
The class DelayedTasker maintains a delayqueue e< DelayedTask> Queue, where the DelayedTask implements the Delayed interface and is defined by an inner class. Both the external class and the internal class implement the Runnable interface. For the external class, its run method is to fetch the tasks in the queue in a defined sequence of time, and these tasks are instances of the inner class. The run method of the inner class defines the specific logic of each thread.

The essence of this design is to define a time-specific list of thread tasks that can be of any length. Specify the startup time each time a task is added.
DelayedTasker. Java


package com.zj.timedtask;

import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

import java.util.Collection;
import java.util.Collections;
import java.util.Random;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class DelayedTasker implements Runnable {
  DelayQueue<DelayedTask> queue = new DelayQueue<DelayedTask>();

  public void addTask(DelayedTask e) {
    queue.put(e);
  }

  public void removeTask() {
    queue.poll();
  }

  public Collection<DelayedTask> getAllTasks() {
    return Collections.unmodifiableCollection(queue);
  }

  public int getTaskQuantity() {
    return queue.size();
  }

  public void run() {
    while (!queue.isEmpty())
      try {
       queue.take().run();
      } catch (InterruptedException e) {
       System.out.println("Interrupted");
      }
    System.out.println("Finished DelayedTask");
  }

  public static class DelayedTask implements Delayed, Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private final int delta;
    private final long trigger;

    public DelayedTask(int delayInSeconds) {
      delta = delayInSeconds;
      trigger = System.nanoTime() + NANOSECONDS.convert(delta, SECONDS);
    }

    public long getDelay(TimeUnit unit) {
      return unit.convert(trigger - System.nanoTime(), NANOSECONDS);
    }

    public int compareTo(Delayed arg) {
      DelayedTask that = (DelayedTask) arg;
      if (trigger < that.trigger)
       return -1;
      if (trigger > that.trigger)
       return 1;
      return 0;
    }

    public void run() {
      //run all that you want to do
      System.out.println(this);
    }

    public String toString() {
      return "[" + delta + "s]" + "Task" + id;
    }
  }

  public static void main(String[] args) {
    Random rand = new Random();
    ExecutorService exec = Executors.newCachedThreadPool();
    DelayedTasker tasker = new DelayedTasker();
    for (int i = 0; i < 10; i++)
      tasker.addTask(new DelayedTask(rand.nextInt(5)));
    exec.execute(tasker);
    exec.shutdown();
  }
}

Results:


[0s]Task 1
[0s]Task 2
[0s]Task 3
[1s]Task 6
[2s]Task 5
[3s]Task 8
[4s]Task 0
[4s]Task 4
[4s]Task 7
[4s]Task 9
Finished DelayedTask

3. Java. Util. Concurrent. ScheduledThreadPoolExecutor
This class can be scheduled to run tasks (threads) after a given delay or to execute tasks periodically (repeatedly). You need to know the size of the thread pool in the constructor. The main approach is:

[1] the schedule
Public ScheduledFuture< The & # 63; > Schedule (Runnable command, long delay,TimeUnit unit)
Creates and performs a one-time operation that is enabled after a given delay.
Specify:
- schedule in interface ScheduledExecutorService;
Parameters:
-command - the task to be performed;
-delay - the time to delay the execution from now on;
-unit -unit of time for delay parameters;
Returns:
- represents the ScheduledFuture of pending task completion, and its get() method returns null when completed.
 
[2] scheduleAtFixedRate
Public ScheduledFuture< The & # 63; > ScheduleAtFixedRate (
Runnable command,long initialDelay,long period,TimeUnit)
Creates and performs a periodic operation that is enabled for the first time after a given initial delay, followed by a given period; That is, it will execute after the initialDelay, then after the initialDelay+period, then after the initialDelay+ 2 * period, and so on. If any execution of a task encounters an exception, subsequent executions are canceled. Otherwise, the task can only be terminated by executing the program's cancel or terminate method. If any execution of this task takes longer than its cycle, subsequent execution will be delayed, but not executed simultaneously.
Specify:
- scheduleAtFixedRate in interface ScheduledExecutorService;
Parameters:
-command - the task to be performed;
-initialDelay - the delay time for the first execution;
-period -period between consecutive executions;
-unit - the time unit of the initialDelay and period parameters;
Returns:
- represents the ScheduledFuture of pending task completion, and its get() method throws an exception after cancellation.
4. Design a thread executor with a time delay feature
Class a ScheduledThreadPoolExcutor ScheduleTasked associate, can specify the size of the thread pool. Use the schedule method to know the thread and delay time, and use the shutdown method to close the thread pool. There is some flexibility in the logic for specific tasks (threads) (compared to the previous design, the previous design must define the logic of the thread in advance, but the thread-specific logic design can be modified by inheritance or decoration).
ScheduleTasker. Java


package com.zj.timedtask;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ScheduleTasker {
  private int corePoolSize = 10;
  ScheduledThreadPoolExecutor scheduler;

  public ScheduleTasker() {
    scheduler = new ScheduledThreadPoolExecutor(corePoolSize);
  }

  public ScheduleTasker(int quantity) {
    corePoolSize = quantity;
    scheduler = new ScheduledThreadPoolExecutor(corePoolSize);
  }

  public void schedule(Runnable event, long delay) {
    scheduler.schedule(event, delay, TimeUnit.SECONDS);
  }

  public void shutdown() {
    scheduler.shutdown();
  }

  public static void main(String[] args) {
    ScheduleTasker tasker = new ScheduleTasker();
    tasker.schedule(new Runnable() {
      public void run() {
       System.out.println("[1s]Task 1");
      }
    }, 1);
    tasker.schedule(new Runnable() {
      public void run() {
       System.out.println("[2s]Task 2");
      }
    }, 2);
    tasker.schedule(new Runnable() {
      public void run() {
       System.out.println("[4s]Task 3");
      }
    }, 4);
    tasker.schedule(new Runnable() {
      public void run() {
       System.out.println("[10s]Task 4");
      }
    }, 10);

    tasker.shutdown();
  }
}

Results:


[1s]Task 1
[2s]Task 2
[4s]Task 3
[10s]Task 4
  public void run() {
   try {
    TimeUnit.SECONDS.sleep(3);
    System.out.println("i'm going.");
    monitor.gotMessage();
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
}

class Monitor implements Runnable {
  private volatile boolean go = false;

  public synchronized void gotMessage() throws InterruptedException {
   go = true;
   notify();
  }

  public synchronized void watching() throws InterruptedException {
   while (go == false)
    wait();
   System.out.println("He has gone.");
  }

  public void run() {
   try {
    watching();
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
}

public class Wait {
  public static void main(String[] args) {
   Monitor monitor = new Monitor();
   MyObject o = new MyObject(monitor);
   new Thread(o).start();
   new Thread(monitor).start();
  }
}

Results:


i'm going.
He has gone.


Related articles: