Create a new Thread object and call the method start(). The call to the start() method will return immediately, and the thread will start executing the run() method asynchronously.
When extending the Thread class, the run() method should be overridden to provide the code executed by the thread. This is analogous to implementing the run() method of the Runnable interface.
(b) and (e)
The Thread class implements the Runnable interface and is not abstract. A program terminates when the last non-daemon thread ends. The Runnable interface has a method named run, but the interface does not dictate that implementations must define a method named start. Calling the run() method on a Runnable object does not necessarily create a new thread. Method run() is the method executed by a thread. Instances of the Thread class must be created to spawn new threads.
The program will compile without errors, and will simply terminate without any output when run. Two thread objects will be created, but they will never be started. The start() method must be called on the thread objects to make the threads execute the run() method asynchronously.
(a) and (e)
Because the exact behavior of the scheduler is undefined, the text A, B, and End can be printed in any order. The thread printing B is a daemon thread, which means that the program may terminate before the thread manages to print the letter.
No two threads can concurrently execute synchronized methods on the same object. This does not prevent one thread from executing a non-synchronized method while another thread executes a synchronized method on the same object. The synchronization mechanism in Java acts like recursive semaphores, which means that during the time a thread owns the lock, it may enter and re-enter any region of code associated with the lock.
One cannot be certain whether any of the letters i, j, and k will be printed during execution. For each invocation of the doit() method, each variable pair is incremented and their values are always equal when the method returns. The only way a letter could be printed would be if the method check() was executed between the time the first and the second variable were incremented. Since the check() method does not depend on owning any lock, it can be executed at any time, and the method doit() cannot protect the atomic nature of its operations by acquiring locks.
A thread dies when the execution of the run() method ends. The call to the start() method is asynchronous, that is, it returns immediately, and it enables the thread for running. Calling the sleep() or wait() methods will only block the thread temporarily.
(b) and (d)
The inner createThread() call is evaluated first, and will print 23 as the first number. The last number the main thread prints is 14. After the main thread ends, the thread created by the inner createdThread() completes its join() call and prints 22. After this thread ends, the thread created by the outer createThread() call completes its join() call and prints the number 12 before the program terminates.
The exact behavior of the scheduler is not defined. There is no guarantee that a call to the yield() method will grant other threads use of the CPU.
The notify() method is defined in the Object class.
The priority of a thread is set by calling the setPriority() method in the Thread class. No Thread constructor accepts a priority level as an argument.
(a) and (c)
A thread can hold multiple locks; for example, by nesting synchronized blocks. Invoking the wait() method on an object whose lock is held by the current thread will relinquish the lock for the duration of the call. The notify() method does not relinquish any locks.
An IllegalMonitorStateException will be thrown if the wait() method is called when the current thread does not hold the lock of the object.
(a), (c), (d), and (e)
The thread terminates once the run() method completes execution.