Skip to content

Threads

William Wood edited this page Dec 17, 2019 · 1 revision

Threads

Java and Kotlin allow you to easily spawn multiple threads in your program, allowing you to execute separate pieces of code in parallel. Either by manually creating each thread and setting it going, or automatically in something like a parallel for loop where each iteration of the loop is run on separate threads in parallel.

Python doesn't normally let you do this. It has some ability to schedule code to run on a single thread so that it looks like it's multi-threading, but in reality it is only using one thread and thus can only be using one processor core. However, since you are using JISA you must be using Jython (or something equivalent), which gives you access to Java threads. This is, generally, why Jython exists: to take advantage of the technical superiority of Java from within Python.

Thread Objects

The simplest for of threading is by creating Thread objects. These are very simple. You create them by giving them a Runnable (see "Functions as Objects") to run and then start them.

For example:

Java

Thread myThread = new Thread(() -> {

  int total = 1;

  for (int i = 1; i <= 100; i++) {
    total *= i;
  }

  System.out.printf("100! = %d\n", total);

});

myThread.start();

Kotlin

val myThread = Thread {

  var total = 1

  for (i in 1..100) {
    total *= i
  }

  println("100! = $total")

}

myThread.start()

Jython

from java.lang import Thread

def calcFactorial():

  total = 1

  for i in range(1, 101, 1):
    total *= i

  print("100! = %d" % total)


myThread = Thread(calcFactorial)
myThread.start()

When we call start() on this thread it will start calculating 100 factorial and then print out the result. When it's done the thread shuts down. Anything we write after myThread.start() will run immediately since the code in our Thread object is running on a separate thread.

To illustrate this, we can set two threads off simultaneously:

Thread thread1 = new Thread(() -> {

  while (true) {
    System.out.println("This is from thread 1!");
    Util.sleep(500);
  }

});

Thread thread2 = new Thread(() -> {

  while (true) {
    System.out.println("This is from thread 2!");
    Util.sleep(430);
  }

});

thread1.start();
thread2.start();

The result will be both the lines "This is from thread 1!" and "This is from thread 2!" being output at intervals of 500 ms and 430 ms respectively.

Auto-Threading

If you have a Collection of some sort, for example a List such as an ArrayList or LinkedList, then you can iterate over it in parallel by use of the parallelStream feature.

List<Double> myList = new LinkedList<>();
myList.add(1.0); // etc

// This will perform a for-each loop but on parallel streams (if possible)
myList.parallelStream().forEach((v) -> {
  System.out.println(v);
});

This will perform the action defined in the lambda function for each element in myList, automatically creating new threads to carry it out (the number based on how many processor cores you have). This will block until all these automatically created threads have finished their work. As a result of each element being processed in parallel, it might not necessarily be processed in the order that the items are listed in. It will come down to which thread gets there first. For example, trying this with a list of [1.0, 2.0, 3.0, 4.0, 5.0] processed them in the order [3.0, 1.0, 2.0, 4.0, 5.0]. If run again, it would likely be in a different order again.

This functionality is available in both Kotlin and Jython due to their ability to use Java's Collection classes:

Kotlin

list.parallelStream().forEach { println(it) }

Python

list.parallelStream().forEach(lambda v: print(v))

Semaphores

To help with some synchronisation, Java provides the Semaphore class. These objects act as co-ordinators, holding back code until it is ready to be run. They work based on a system of "permits". Each object has a number of permits it can hand out at any given time. If a thread attempts to acquire one from a Semaphore but it has none to give, the thread will block until it has one.

Scheduling

Java lets you schedule events to happen using Timer and TimerTask objects. A Timer object is what does the scheduling, deciding when to run a task whereas a TimerTask is the task to run itself. For example:

TimerTask task = new TimerTask() {

  void run() {
    System.out.println("The task has been run!");
  }

};

Timer timer = new Timer();

// Schedule task to run 5000 ms from now
timer.schedule(task, 5000);

the output:

* Program Starts              *
* Blank Console for 5 seconds *

The task has been run!

* Program Terminates          *
Clone this wiki locally