Three ways to create a thread
1. Inherit the Thread class
public class ThreadTest01 extends Thread {
@Override
public void run(a) {
for (int i = 0; i < 10; i++) {
System.out.println("run--->"+ i); }}public static void main(String[] args) {
Thread thread = new ThreadTest01();
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("main====>"+ i); }}}Copy the code
Not recommended to avoid the limitations of OOP single inheritance
2. Implement the Runnable interface
public class ThreadTest02 implements Runnable {
@Override
public void run(a) {
for (int i = 0; i < 10; i++) {
System.out.println("run--->"+ i); }}public static void main(String[] args) {
ThreadTest02 t = new ThreadTest02();
Thread thread = new Thread(t);
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("main====>"+ i); }}}Copy the code
Recommended use: Avoid the limitation of single inheritance, flexible and convenient, convenient to use the same object by multiple threads
Simplify code with anonymous inner classes:
interface Test {
void fun(a);
}
public class AnonymousClassesTest {
public static void main(String[] args) {
// Anonymous inner class
Test test = new Test() {
public void fun(a) {
System.out.println("Anonymous inner class"); }}; test.fun();// Output: anonymous inner class
// Create threads using anonymous inner classes
new Thread(new Runnable() {
@Override
public void run(a) {
System.out.println("abc");
}
}).start(); // abc
}
Copy the code
Use lambda expressions to simplify code: any interface that contains only one abstract method is a functional interface. For functional interfaces, we can create objects for that interface through lambda expressions.
interface Test {
void fun(a);
}
public class LambdaTest {
public static void main(String[] args) {
// lambda expressions
// The contents of the braces are equivalent to overriding the methods in the interface. If the code is only one line, you can remove the braces
test = () -> {
System.out.println("i like lambda");
};
test.fun(); // Output: I like lambda
// Use lambda expressions to create threads
new Thread( () -> System.out.println("abc") ).start(); // abc}}Copy the code
3. Implement the Callable interface
public class ThreadTest04 implements Callable<String> {
@Override
public String call(a) throws Exception {
return "Return value";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> c1 = new ThreadTest04();
Callable<String> c2 = new ThreadTest04();
Callable<String> c3 = new ThreadTest04();
// Create the execution service
ExecutorService ser = Executors.newFixedThreadPool(3);
// Commit execution
Future<String> f1 = ser.submit(c1);
Future<String> f2 = ser.submit(c2);
Future<String> f3 = ser.submit(c3);
// Get the result
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
// Shut down the serviceser.shutdown(); }}Copy the code
Priority of the thread
In Java threads, priority is controlled by an integer member variable priority, which ranges from 1 to 10. The default priority is 5. The priority can be set by setPriority(int). Threads with a higher priority allocate more time slices than threads with a lower priority. However, thread priority cannot be used as a dependency on program correctness.
Six states of Java threads
- NEW: Initial state, the thread is built, but the start() method has not been called
- Java threads refer to the ready and running states of the operating system as RUNNABLE.
- BLOCKED: the thread is BLOCKED in a lock
- WAITING: a state in which the current thread is WAITING for some specific action (notification or interrupt) from another thread
- TIME_WAITING: a timeout WAITING state that, unlike WAITING, can return at a specified time
- TERMINATED: indicates that the current thread is TERMINATED
The blocking state is the state in which a thread blocks when it enters a method or code block modified by the synchronized keyword (obtaining the Lock), whereas threads blocking the Lock interface in the Java.concurrent package are in the wait state. Because the Lock interface in the Java.Concurrent package uses the related methods in the LockSupport class for blocking implementations.
Daemon thread
A Daemon is a support thread because it is used for background scheduling and support work in applications. This means that a Java virtual machine will exit when there are no non-Daemon threads in the machine. So when building Daemon threads, you can’t rely on the contents of the finally block to ensure that the logic to close or clean up resources is performed, because the contents of the finally block are not necessarily executed. The thread can be set to a Daemon thread by calling setDaemonn(true) (the default is false).
public class TestDaemon {
public static void main(String[] args) {
Thread thread = new Thread(new MyDaemon(), "Daemon thread");
thread.setDaemon(true); thread.start(); }}class MyDaemon implements Runnable {
@Override
public void run(a) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("I'm the daemon thread"); }}}Copy the code
Executing the above code will find no print.
interrupt
A thread terminates automatically when it finishes executing, or prematurely if an exception occurs during execution. InterruptedException is thrown to terminate a thread prematurely by calling interrupt() on the thread if it is blocked, waiting, or timed out. However, I/O and synchronized lock blocking cannot be interrupted. A thread checks for interruption by using isInterrupted(). If the thread has been interrupted, this method returns false even if it has been interrupted.
public class Interrupted {
public static void main(String[] args) throws InterruptedException {
// sleepThread keeps trying to sleep
Thread sleepThread = new Thread(new SleepRunner(), "sleepThread");
// BusyThread keeps running
Thread busyThread = new Thread(new BusyRunner(), "busyThread");
sleepThread.setDaemon(true);
busyThread.setDaemon(true);
sleepThread.start();
busyThread.start();
// Sleep for 5 seconds to allow sleepThreads and busyThreads to run fully
Thread.sleep(5000);
sleepThread.interrupt();
busyThread.interrupt();
System.out.println("sleepThread interrupted is " + sleepThread.isInterrupted()); // false
System.out.println("busyThread interrupted is " + busyThread.isInterrupted()); // true
// Prevent sleepThreads and busythreads from exiting immediately
Thread.sleep(2000); }}class SleepRunner implements Runnable {
@Override
public void run(a) {
while (true) {
try {
Thread.sleep(10000);
} catch(InterruptedException e) { e.printStackTrace(); }}}}class BusyRunner implements Runnable {
@Override
public void run(a) {
while (true) {}}}Copy the code
Results: sleepThread interrupted is false busyThread interrupted is true Java. Lang. InterruptedException: sleep interrupted
If a sleepThread is in the timeout waiting state and is interrupted, an exception will be thrown and the thread will be terminated prematurely. In this case, it checks whether the thread is interrupted and returns false.
Wait/notification mechanism
One thread modifies the value of an object, and another thread senses the change and acts accordingly, starting in one thread and ending in another. The former is the producer, while the latter is the consumer. This pattern separates the “what” and “how”, achieves decoupling at the functional level and scales well architecturally. Java implements similar functionality through a wait/notification mechanism. The wait/notification related methods are available to any Java Object, because they are defined on the superclass java.lang.object of all objects, with the following methods:
- Notify () : Notifies a thread waiting on an object to return from wait() if the thread has acquired the lock on the object
- NotifyAll () : Notifies all threads waiting on the object
- Wait () : The thread that called wait() is in a WAITING state. The thread that called wait() is WAITING for notification from another thread or is interrupted. Note that after calling wait(), the object lock is released.
- Wait (long) : wait for more than a certain amount of time (n milliseconds) and return if no notification is given.
- Wait (long, int) : For more granular control of the timeout, up to nanoseconds.
The wait/notification mechanism refers to that thread A calls the wait() method of object O to enter the wait state, and another thread B calls the notify() or notifyAll() method of object O. After receiving the notification, thread A returns from the wait() method of object O and performs subsequent operations.
import java.text.SimpleDateFormat;
import java.util.Date;
public class WaitNotify {
static Object lock = new Object();
static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
Thread waitThread = new Thread(new Wait(), "waitThread");
waitThread.start();
Thread.sleep(1000);
Thread notifyThread = new Thread(new Notify(), "notifyThread");
notifyThread.start();
}
static class Wait implements Runnable {
@Override
public void run(a) {
synchronized (lock) {
// If the condition is not met, continue to wait and release the lock
while (flag) {
try {
System.out.println(Thread.currentThread().getName() + " flag is true wait @ " +
new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.wait();
} catch(InterruptedException e) { e.printStackTrace(); }}// If the conditions are met, the work is completed
System.out.println(Thread.currentThread().getName() + "flag is false running @ " +
new SimpleDateFormat("HH:mm:ss").format(newDate())); }}}static class Notify implements Runnable {
@Override
public void run(a) {
synchronized (lock) {
// Obtain the lock of the lock, and notify. The notification will not release the lock
System.out.println(Thread.currentThread().getName() + "hold lock notify @ " +
new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.notifyAll();
flag = false;
try {
Thread.sleep(2000);
} catch(InterruptedException e) { e.printStackTrace(); }}// Lock again
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "hold lock again @ " +
new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Copy the code
Execution Result: waitThread flag is true wait @ 17:33:39 notifyThreadhold lock notify @ 17:33:40 notifyThreadhold lock again @ 17:33:42 waitThreadflag is false running @ 17:33:44Copy the code
The output order of the third and fourth lines might be reversed.
The above examples illustrate the details of calling wait(), notify(), and notifyAll() :
- Using wait(), notify(), and notifyAll() requires locking the calling object first.
- After calling wait(), the thread state changes from RUNNING to WAITING and the current thread is placed on the object’s wait queue.
- After notify() or notifyAll() is called, the waiting thread does not release the lock from wait() until the thread that calls notify() or notifyAll() releases the lock.
- The notify() method moves one thread in the wait queue from the wait queue to the synchronization queue, and notifyAll() method moves all threads in the wait queue to the synchronization queue, and the status of the moved thread changes from WAITING to BLOCKED.
- The return from wait() is conditional upon obtaining the lock of the calling object.
Wait and notify
The classic paradigm of wait/notification
The waiting party (consumer) shall abide by the following principles:
- Gets the lock of the object
- If the condition is not met, the object’s wait() method is called and the condition is still checked after being notified
- If the conditions are met, the corresponding logic is executed
Synchronized (object) {while(condition not satisfied) {object.wait (); } Corresponding processing logic}Copy the code
The notifying party (producer) shall observe the following principles:
- Get the lock of the object
- Change the conditions
- Notifies all threads waiting on an object
Synchronized (object) {change the condition object. NotifyAll ();Copy the code
Pipeline input/output streams
The piped I/O stream differs from the normal file I/o stream or network I/o stream in that it is primarily used for data transfer between threads, and the transfer medium is memory. There are four specific implementations of piped input/output streams: PipedInputStream, PipedOutputStream, PipedReader, and PipedWriter. The first two are byte oriented and the last two are character oriented.
public class Piped {
public static void main(String[] args) throws IOException {
PipedReader reader = new PipedReader();
PipedWriter writer = new PipedWriter();
// Connect the input and output streams, otherwise IOException will be thrown when used
reader.connect(writer);
Thread printThread = new Thread(new Print(reader), "printThread");
printThread.start();
int receive = 0;
try {
while((receive = System.in.read()) ! = -1) { writer.write(receive); }}finally{ writer.close(); }}}class Print implements Runnable {
private PipedReader reader;
public Print(PipedReader reader) {
this.reader = reader;
}
@Override
public void run(a) {
int receive = 0;
try {
while((receive = reader.read()) ! = -1) {
System.out.print((char) receive); }}catch(IOException e) { e.printStackTrace(); }}}Copy the code
Run the program, enter a string, and see the string output as it is: Hello World! Hello World!Copy the code
The use of the Thread. The join ()
If thread A executes thread.join(), it means that thread A is waiting for thread A to terminate before returning from Thread.join (). Thread() provides join() as well as join(long millis) and JOIN (Long millis, int nanos). These two timeout methods indicate that a thread will be returned from the timeout method if it does not terminate within a given timeout period.
public class TestJoin {
public static void main(String[] args) {
Thread one = new Thread(new JoinOne(), "One thread");
Thread two = new Thread(new JoinTwo(one), "Two" thread); one.start(); two.start(); }}class JoinOne implements Runnable {
@Override
public void run(a) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "- >"+ i); }}}class JoinTwo implements Runnable {
private Thread thread;
public JoinTwo(Thread thread) {
this.thread = thread;
}
@Override
public void run(a) {
try {
thread.join(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "- >"+ i); }}}Copy the code
Executing the code above shows that the printing of thread two begins after thread one finishes.
In the source code of Thread.join(), the join() method calls the overloaded join(long millis), passing in 0, so thread.join () and Thread.join(0) are the same. The underlying join method calls the wait() method
Public final synchronized void join() throws InterruptedException {// If the condition is not met, continue waiting while (isAlive()) {wait(0); } // If the condition is met, the method returns}Copy the code