Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Class (computer programming) wikipedia , lookup
Object-oriented programming wikipedia , lookup
Falcon (programming language) wikipedia , lookup
Java (programming language) wikipedia , lookup
Name mangling wikipedia , lookup
Join-pattern wikipedia , lookup
C Sharp syntax wikipedia , lookup
Parallel computing wikipedia , lookup
C Sharp (programming language) wikipedia , lookup
Java performance wikipedia , lookup
Scheduling (computing) wikipedia , lookup
Java Threads (a review, and some new things) ICS432 - Spring 2017 Concurrent and High-Performance Programming Henri Casanova ([email protected]) Threads in Programming Languages ! Almost all programming languages provide constructs/abstractions for writing concurrent programs " ! Java does it like it does everything else, by providing a class (Thread) and an interface (Runnable) " ! even old ones (Modula, Ada, etc.) You create a thread/runnable object, and then start a thread Java provides several higher level abstractions, which we’ll see later in the semester " It’s important to first master the “low level” stuff Extending the Thread class ! One option: extend the thread class and override its “run()” method public class MyThread extends Thread { public void run() { . . . } . . . } myThread t = new MyThread(); ! But the preferred method is to use the Runnable interface… The Runnable Interface ! Using the Runnable interface is preferred because then you can still extend another class " Java doesn’t have multiple inheritance, so typically " So if you can use an implements instead of an extends, you should ! ! So that you keep that option open for another purpose Let’s see an example… Runnable Example public class RunnableExample { } class MyTask implements Runnable { public void run() { for (int i=0; i<500; i++) System.out.print("#"); } } public RunnableExample() { Thread t = new Thread(new MyTask()); t.start(); for (int i=0; i<500; i++) System.out.print("."); } public static void main(String args[]) { RunnableExample p = new RunnableExample(); } Spawning a thread ! ! To launch, or spawn, a thread, you just call the thread’s start() method WARNING: Don’t call the run() method directly to launch a thread " If you call the run() method directly, then you just call some method of some object, and the method executes ! " Fine, but probably not what you want The start() method, which you should not override, does all the thread launching ! ! It launches a thread that starts its execution by calling the run() method It gets the OS to do all its things to start a new thread What happens ! The previous program runs as a Java process " ! ! that is, a thread running inside the JVM When the start() method is called, the main thread creates a new thread We now have two threads The “main”, “original” thread " The newly created thread " ! Both threads are running The main thread prints ‘.’ 500 times to the screen " The new thread prints ‘#’ 500 times to the screen and exits " ! When both threads are finished, then the process terminates What happens Host JVM process Your program’s main thread Newly created thread On this laptop On a Linux Box Non-deterministic Execution! ! ! Running our simple program shows one difficulty with multithreaded programming, and especially for debugging: it is difficult to tell what the execution will look like The OS schedules when a thread runs " ! ! In ICS332 you learned all the “smarts” implemented in the kernel to schedule threads efficiently But Java provides ways to influence thread scheduling at the JVM level Let’s see what we can do... The Thread class package java.lang; public class Thread implements Runnable { public void start(); public void run(); public boolean isAlive(); public Thread.State getState(); public static void sleep(long millis); public static void sleep(long millis, long nanos); // A bunch of other things we’ll discuss later ... } The isAlive() Method ! ! When you spawn a thread you may not really know when or how it is going to terminate It may be useful to know " ! ! To see if the thread’s work is done for instance The isAlive() method returns true is the thread is running, false otherwise Could be useful to restart a thread if (!t.isAlive()) { t.start(); } The getState() method ! The possible thread states are " " NEW: A thread that hasn’t been started yet RUNNABLE: The thread can be run, and may be running as we speak ! " BLOCKED: The thread is blocked on a monitor ! " See future lecture TIMED_WAITING: The thread is waiting for another thread to do something, but will give up after a specified time out ! " See future lecture WAITING: The thread is waiting for another thread to do something ! " It might not because another runnable thread could be running See future lecture TERMINATED: The thread’s run method has returned ! isAlive() would return false Thread Lifecycle: 4 states RUNNABLE NEW running not running TERMINATED BLOCKED/ WAITING/ TIMED_WAITING Thread Lifecycle: 4 states RUNNABLE NEW start() running not running TERMINATED BLOCKED/ WAITING/ TIMED_WAITING Thread Lifecycle: 4 states sleep() block on I/O wait() RUNNABLE NEW start() running not running TERMINATED BLOCKED/ WAITING/ TIMED_WAITING Thread Lifecycle: 4 states sleep() block on I/O wait() RUNNABLE NEW start() running not running BLOCKED/ WAITING/ TIMED_WAITING time elapsed I/O done notify() TERMINATED Thread Lifecycle: 4 states sleep() block on I/O wait() RUNNABLE NEW start() running not running run() method returns TERMINATED BLOCKED/ WAITING/ TIMED_WAITING time elapsed I/O done notify() Java Threads / Kernel Threads ! Each Java thread is mapped to a kernel thread scheduler thread application threads O/S JVM Java Threads / Kernel Threads ! This gets a bit complicated The JVM has a thread scheduler for application threads, which are mapped to kernel threads " The O/S also has a thread scheduler for kernel threads " ! ! The JVM is itself multithreaded! We have threads everywhere Application threads in the JVM " Kernel threads that run application threads " Threads in the JVM that do some work for the JVM " ! ! Let’s look at a running JVM that runs a program that does nothing and count threads.... Let’s now run a program that runs a program that creates a thread and does nothing and count threads... The JVM is Multithreaded ! The previous experiment shows that the JVM is highly multithreaded " ! 22 threads besides your own program, with my version of Java (jdk 1.8.0_65, Mac OSX) What are these 22 threads doing??? " They are called “daemon threads” ! ! Get terminated whenever non-daemon threads are all finished Can actually be created by a Java program One of them has to be the “JVM main thread” (my program) " The Garbage collector is a daemon thread " There is an important thread called “The Event Dispatch Thread” that we’ll talk about " And many others... " So what? ! ! ! At this point, it seems that we throw a bunch of threads in, and we don’t really know what happens To some extent it’s true, but we have ways to have some control In particular, what happens in the RUNNABLE state? RUNNABLE running ! not running Can we control how RUNNABLE threads become running or not running? ! This is typically controlled by the OS via scheduling The yield() method: example ! With the yield() method, a thread will pause and give other RUNNABLE threads the opportunity to execute for a while public class MyThread extends Thread { public void run() { for (int i=0; i<5000; i++) { System.out.print(“#”); Thread.yield(); } } } public class MyProgram { public MyProgram() { MyThread t = new MyThread(); t.start(); for (int i=0; i<5000; i++) { System.out.print(“.”); Thread.yield(); } } public static void main(String args[]) { MyProgram p = new MyProgram(); } } Does Yield work? ! Let’s try the previous code and the same code without the calls to yield, trying to see if we have more flip-flopping with the yield calls... Does Yield work? ! Let’s try the ‘#’ and ‘.’ program with and without the calls to yield, trying to see if we have more flipflopping with the yield calls... ! Let’s do the same but adding busy work to the threads... Does Yield work? ! Let’s try the ‘#’ and ‘.’ program with and without the calls to yield, trying to see if we have more flipflopping with the yield calls... ! Let’s do the same but adding busy work to the threads... ! Conclusion: Thread.yield() does influence execution, but we can’t rely on it for correctness ! Many Java developers try to refrain from using yield() Thread Priorities ! The Thread class has a setPriority() and a getPriority() method " ! A new Thread inherits the priority of the thread that created it Thread priorities are integers ranging between Thread.MIN_PRIORITY and Thread.MAX_PRIORITY " The higher the integer, the higher the priority Thread Priorities and Scheduling ! Whenever there is a choice between multiple runnable threads, the JVM should pick the higher priority one " ! The JVM is preemptive " ! High priority threads can decide to call yield() to prevent starvation of low-priority threads If a new higher priority thread is started, it gets to run now Modern JVMs use time slicing Threads of the highest priorities get chosen in a round-robin fashion " The use of yield() isn’t required but, as we saw, it can increase the frequency of switching between threads " ! In spite of all this: The JVM can only influence the way in which threads are scheduled ! And some JVM implementations ignore priorities! " Ultimately, the decision is left to the OS " So what? ! It is important to know the basics of thread scheduling to understand the behavior of concurrent programs ! One should NEVER rely on scheduling aspects to ensure correctness of the program Since scheduling depends on the JVM and on the OS, correctness due to scheduling is not portable or deterministic " The behavior on one system may be wildly different on another, so you can never be sure " The join() method ! ! ! The join() method causes a thread to wait for another thread’s termination This is useful for “dispatching” work to a worker thread and waiting for it to be done Let’s see it used on an example A join() example public class JoinExample { public JoinExample() { Thread t1 = new Thread1(); t1.start(); System.out.println("Parent thread: waiting for child to finish"); try { t1.join(); } catch (InterruptedException e) {} System.out.println("Parent thread: child has finished"); } public static void main(String args[]) { JoinExample j = new JoinExample(); } private class Thread1 extends Thread { public void run() { for (int i=0; i<5; i++) { System.out.println("Child thread: iteration "+i); try { Thread.sleep(1000); } catch (InterruptedException e) {} } } } } Conclusion ! Best way to create threads in Java: implement the Runnable interface and then create a new Thread object ! Thread Scheduling is complex, not fully deterministic, and must not be counted on to guarantee program correctness ! We can look at Exercise #1 from our programming assignment… For Exercise #2, we have to know a little bit about Swing… !