Multithreading in Java

Thread:

 Thread thread = new Thread() {
     @Override
     public void run(a) {
         System.out.println("Thread started!"); }}; thread.start();Copy the code

Several common methods of the Thread class:

  • Sleep () : static method that causes the current thread to sleep for a period of time;
  • CurrentThread () : static method that returns a reference to the thread object currently executing;
  • Start () : the Java virtual machine calls the run() method in the thread to start execution.
  • Join () : make the current thread wait for another thread to complete the execution before continuing, internal call is implemented by Object class wait method;
  • Yield () : Yield means to give up, and the yield() method means that the current thread is willing to relinquish possession of the current processor. Note that even if the current thread calls yield() to yield a processor, scheduling may continue to allow that thread to compete to acquire the processor and run;

A Runnable:

Runnable is a functional interface:

@FunctionalInterface
public interface Runnable {
    public abstract void run(a);
}
Copy the code

Runnable is not conducive to thread reuse management

 Runnable runnable = new Runnable() {
     @Override
     public void run(a) {
         System.out.println("Thread with Runnable started!"); }}; Thread thread =new Thread(runnable);
 thread.start();
Copy the code

ThreadFactory:

    ThreadFactory factory = new ThreadFactory() {
        int count = 0;
        @Override
        public Thread newThread(Runnable r) {
            count ++;
            return new Thread(r, "Thread-"+ count); }}; Runnable runnable =new Runnable() {
        @Override
        public void run(a) {
            System.out.println(Thread.currentThread().getName() + "started!"); }}; Thread thread = factory.newThread(runnable); thread.start(); Thread thread1 = factory.newThread(runnable); thread1.start();Copy the code

Executor thread pool:

Executor thread pools (most recommended) :

 Runnable runnable = new Runnable() {
     @Override
     public void run(a) {
         System.out.println("Thread with Runnable started!"); }}; Executor executor = Executors.newCachedThreadPool(); executor.execute(runnable); executor.execute(runnable); executor.execute(runnable);Copy the code

Callable and Future:

Callable, like Runnable, is a functional interface that has only one abstract method. The difference is that Callable provides methods that return values and support generics.

@FunctionalInterface
public interface Callable<V> {
    V call(a) throws Exception;
}
Copy the code

Callable and Future usually come in pairs. Future.get () gets the result of an asynchronous execution. If no result is available, this method blocks until the asynchronous calculation is complete.

 Callable<String> callable = new Callable<String>() {
        @Override
        public String call(a) {
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Done!"; }}; ExecutorService executor = Executors.newCachedThreadPool(); Future<String> future = executor.submit(callable);try {
        String result = future.get();
        System.out.println("result: " + result);
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }

Copy the code

Thread safety and thread synchronization

Thread-safe: When a function is called in a multi-threaded environment, it can correctly handle global variables between multiple threads to complete the function correctly.

Thread synchronization: When one thread is working on memory, no other thread can work on the memory address.

Thread synchronization does not equal thread safety, and many people today misunderstand this and like to lump them together. The reality is, when I ask interviewees about thread synchronization, many of them say thread safety.

Thread synchronization is one way to achieve thread-safety, but you can certainly achieve thread-safety in other ways.

Threadsafe vs Synchronized

The essence of thread safety is a resource problem:

When a shared resource is read by one thread, the resource cannot be arbitrarily written by other threads.

When a shared resource is written by one thread, it cannot be read or written by other threads.

Here are a few ways to implement thread safety in Java:

synchronized

Synchronized ensures mutually exclusive access to resources (data) within methods or code blocks in a synchronized manner, and ensures data synchronization of monitored resources between threads.

 private synchronized void count(int newValue) {
        x = newValue;
        System.out.println("x= " + x);
    }

Copy the code

Another way of writing:

 private void count(int newValue) {
    synchronized (this) {
        x = newValue;
        System.out.println("x= "+ x); }}Copy the code

volatile

Volatile variables are atomic and synchronized, which is equivalent to mutually exclusive thread access to a single field.

The volatile keyword ensures memory visibility. If a variable declared with volatile changes its value in one thread, the change is immediately visible to other threads.

Volatile can be seen as a simplified version of synchronized.

Volatile is valid only for assignments of primitive types (byte, char, short, int, long, float, double, Boolean) and for referential assignments of objects.

java.util.concurrent.atomic:

AtomicInteger, AtomicBoolean, etc. AtomicInteger, AtomicBoolean, etc. can be regarded as volatile modified Integer, Boolean, etc.

Lock / ReentrantReadWriteLock

Lock is the same locking mechanism, but is more flexible and cumbersome:

Lock lock = newReentrantLock(); . lock.lock();try {
    x++;
} finally {
    lock.unlock();
}

Copy the code

One performance problem with Synchronized is that reads are mutually exclusive, so instead of just using locks, more complex locks such as ReadWriteLock are used for more detailed operations, as follows:

ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();
private int x = 0;
private void writeOperate (a) {
    writeLock.lock();
    try {
        x++;
    } finally{ writeLock.unlock(); }}private void readOperate ( int time){
    readLock.lock();
    try {
        System.out.println();
    } finally{ readLock.unlock(); }}Copy the code

The read lock is shared, so the above code, when there is a write operation, other threads cannot write, cannot read; When this thread reads, other threads cannot write, but can read.

Communication/interaction between threads

Threads have their own private space, but when I have multiple threads cooperating with each other, I need to communicate between threads. This section will introduce several communication principles between Java threads.

Locks and synchronization

This method is mainly to lock global variables, that is, to lock objects or code blocks with synchronized keyword, to achieve inter-thread communication.

See the example in thread synchronization in the previous section for this approach.

Wait/notification mechanism

The lock-based approach requires the thread to constantly try to acquire the lock, which costs the server resources.

Java multithreading wait/notification mechanism is based on the Object class wait() method and notify(), notifyAll() method to achieve,

The wait() and notify() methods must be written inside synchronized blocks:

The wait() and notify() methods must be invoked using the acquired lock object. To wait, a thread releases the lock and sleeps until another thread calls notify() to wake it up. The corresponding notify() is the wake up operation for an object lock and must therefore be placed within the synchronized block environment.

NotifyAll () wakes up all waiting threads at random. NotifyAll () wakes up all waiting threads at random. NotifyAll () wakes up all waiting threads at random and wakes up all waiting threads at random.

public class mythread {

    private static Object lock = new Object();

    static class ThreadA implements Runnable {
        @Override
        public void run(a) {
            synchronized (lock) {
                for (int i = 0; i < 5; i++) {
                    try {
                        System.out.println("ThreadA: " + i);
                        lock.notify();
                        lock.wait();
                    } catch(InterruptedException e) { e.printStackTrace(); } } lock.notify(); }}}static class ThreadB implements Runnable {
        @Override
        public void run(a) {
            synchronized (lock) {
                for (int i = 0; i < 5; i++) {
                    try {
                        System.out.println("ThreadB: " + i);
                        lock.notify();
                        lock.wait();
                    } catch(InterruptedException e) { e.printStackTrace(); } } lock.notify(); }}}public static void main(String[] args) {
        new Thread(new ThreadA()).start();
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(newThreadB()).start(); }}Copy the code

The join method

The join() method leaves the current thread in a “wait” state, waiting for the join thread to complete before resuming execution.

When the main thread creates and starts a time-consuming child thread and the main thread terminates before the child thread completes, the join method can be used to let the main thread retrieve some data that has been processed in the child thread.

Both the Join () method and its overloaded methods make use of wait(long) at bottom.

public class mythread {

    static class ThreadA implements Runnable {

        @Override
        public void run(a) {
            try {
                System.out.println("Child thread sleeps for a second");
                Thread.sleep(1000);
                System.out.println("The child thread is out of sleep for a second.");
            } catch(InterruptedException e) { e.printStackTrace(); }}}public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new ThreadA());
        thread.start();
        thread.join();
        System.out.println("If you don't add join, this line will print out first."); }}Copy the code

Sleep method

The sleep method is a static method of Thread. It is used to put the current thread to sleep for a while:

  • Thread.sleep(long)

It is important to note that **sleep does not release the current lock, while wait does. ** This is one of the most common multithreaded interview questions.

The difference between the sleep and wait methods:

  • Wait specifies the time or not; Sleep must be specified.
  • Wait releases CPU resources and locks. Sleep frees CPU resources but does not release locks, so it is prone to deadlocks.
  • Wait must be placed in a synchronized block or method, while sleep can be anywhere

Ref;

ThreadLocal is a local thread copy variable utility class that can be understood as a thread-local variable or thread-local store. Strictly speaking, the ThreadLocal class does not communicate between multiple threads, but rather allows each thread to have its own “independent” variables that do not affect each other.

The ThreadLocal classes most commonly use set and GET methods. Sample code:

public class mythread {
    static class ThreadA implements Runnable {
        private ThreadLocal<String> threadLocal;

        public ThreadA(ThreadLocal<String> threadLocal) {
            this.threadLocal = threadLocal;
        }

        @Override
        public void run(a) {
            threadLocal.set("A");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ThreadA output:" + threadLocal.get());
        }

        public static void main(String[] args) {
            ThreadLocal<String> threadLocal = new ThreadLocal<>();
            new Thread(newThreadA(threadLocal)).start(); }}}Copy the code

As you can see, ThreadA can access a value of its current thread. If developers want to associate a static variable of a class (user ID or Transaction ID) with thread state, they can consider using ThreadLocal instead of declaring a private variable in each thread to operate on and “reload” the thread.

InheritableThreadLocal is a subclass of ThreadLocal that InheritableThreadLocal can access not only the current thread but also its children.

Semaphore mechanism

The JDK provides a Semaphore class with “Semaphore” functionality. In scenarios where multiple threads (more than two) need to cooperate with each other, simple “locks” and “wait notification mechanisms” are less convenient. This is where the semaphore comes in. Many of the multithreaded communication tool classes provided in the JDK are based on the semaphore model.

The pipe

Pipes are a form of communication based on “pipe flow”. The JDK provides PipedWriter, PipedReader, PipedOutputStream, and PipedInputStream. The first two are character-based and the last two are byte stream based.

Application scenario: Pipes are mostly related to I/O flows. Pipes are used when one thread needs to send a message (such as a string) or a file before another thread.

public class Pipe {
    static class ReaderThread implements Runnable {
        private PipedReader reader;

        public ReaderThread(PipedReader reader) {
            this.reader = reader;
        }

        @Override
        public void run(a) {
            System.out.println("this is reader");
            int receive = 0;
            try {
                while((receive = reader.read()) ! = -1) {
                    System.out.print((char)receive); }}catch(IOException e) { e.printStackTrace(); }}}static class WriterThread implements Runnable {

        private PipedWriter writer;

        public WriterThread(PipedWriter writer) {
            this.writer = writer;
        }

        @Override
        public void run(a) {
            System.out.println("this is writer");
            int receive = 0;
            try {
                writer.write("test");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    writer.close();
                } catch(IOException e) { e.printStackTrace(); }}}}public static void main(String[] args) throws IOException, InterruptedException {
        PipedWriter writer = new PipedWriter();
        PipedReader reader = new PipedReader();
        writer.connect(reader); // You must connect

        new Thread(new ReaderThread(reader)).start();
        Thread.sleep(1000);
        new Thread(newWriterThread(writer)).start(); }}/ / output:
this is reader
this is writer
test
Copy the code

Reference:

concurrent.redspider.group/

www.matools.com/api/java8