Five years after graduation, with an annual salary of one million, I have mastered the simplest and rudimentary methods of getting started with java

Posted by livvylove on Thu, 10 Feb 2022 11:27:18 +0100

hello everyone! I am an old ape. I love technology. I have been in the Java industry for 7 years and am on the way to learn and share every day!

thread

1. Introduction

As we all know, multithreading brings us better resource utilization and better program response, so I won't elaborate on its introduction. You can search by yourself. I'm mainly talking about the key point

  • Threads are independent execution paths;
  • When the program is running, even if it does not create its own thread, there will be multiple threads in the background, such as main thread and gc thread;
  • When operating on the same resource, there will be the problem of resource grabbing, and concurrency control needs to be added;
  • Threads will bring additional overhead, such as cpu scheduling time and concurrency control overhead;
  • Each thread interacts in its own working memory, and improper memory control will cause inconsistent data;

2. Thread creation

There are three ways to create threads:

  1. Inherit the Thread class and override the run() method
  2. Implement the Runnable interface and rewrite the run() method
  3. Implement the Callable interface (you can set the return value), and override the call() method
//Inherit the tree class and override the run() method
public class MyThread extends Thread {
	@Override
    public void run() {
        System.out.println("This is an inheritance Thread Class.");
    }
    
    public static void main(String [] args) {
        //Thread startup requires the start() method instead of run()
        new MyThread().start();
    }
}
//Implement the Runnable interface and rewrite the run() method
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("This is an implementation Runnable Thread of the interface.");
    }
    
    public static void main(String [] args) {
        //The Runnable interface needs to create a Thread through the Thread class
        new Thread(new MyRunnable()).start();
    }
}
//Implement the Callable interface and rewrite the call() method
public class MyCallable implements Callable<E> {
    @Override
    public E call() {
        System.out.println("This is an implementation Callable Thread of the interface.");
        return new E();
    }
    
    public static void main(String [] args) throws ExecutionException, InterruptedException {
        //The first method creates a Callable thread
        //Create thread pool
        ExecutorService ser = Executors.new FixedThreadPool(3);
        //Submit the callable < E > implementation class to the thread pool and return the future < E > class
        Future<E> future = ser.submit(new MyCallable());
        //Get the return value of the call() method through the get() method
        E e = future.get();
        //Close thread pool
        ser.shutdownNow();
        
        //The second method creates a Callable thread
        FutureTask<E> futureTask = new FutureTask<E>(new MyCallable());
        new Thread(futureTask).start();
        //If you do not run through start() first, the program will not be able to stop.
        System.out.println(futureTask.get());
    }
}


It is recommended to implement the Runnable interface because of the limitation of single inheritance in Java, and the interface can inherit multiple, so that the same object can be used by multiple threads.

3. Thread

  • Create a thread with status new.
  • The ready state is that the thread calls the start() method. At this time, the thread immediately enters the ready state, but it does not mean that it is scheduled to run by the cpu immediately.
  • The running state is that the thread is scheduled by the cpu, and the cpu resources are obtained at this time.
  • The blocking state is that the thread is stopped. For example, when the thread calls sleep(), wait(), and synchronization locking, the thread enters the blocking state, that is, the code cannot continue to execute downward. After the blocking state is released, it will re-enter the ready state and wait for the cpu to schedule execution.
  • The dead state is that the thread is suspended and ended. Once it enters the dead state, it cannot be started again.

3.1 thread method and description

  • In addition, there are stop() and destroy() methods provided by JDK. [abandoned]
  • It is recommended to stop the thread itself.
  • It is recommended to use a flag bit to terminate the variable. When the flag is false, the thread will be terminated.

3.2 thread sleep()

  • sleep specifies the number of milliseconds that the current thread is blocked;
  • The slee() method is a static method of Thread class;
  • There is a detected exception InterruptedException in sleep;
  • After the sleep time reaches, the thread enters the ready state;
  • sleep can simulate network delay, countdown and so on;
  • Each object has a lock, but sleep does not release the lock;
  • In which thread the sleep method is called, which thread will be stopped; For example, the main thread calls thread Sleep(), in which the main thread is suspended instead of the thread thread;

3.3 thread yield()

  • Thread concession, so that the currently executing thread is suspended, but not blocked;
  • The yield() method is a static method of Thread class, which will not release the lock;
  • Change the thread from running state to ready state;
  • Let the cpu reschedule, so concessions may not be successful! Just let threads compete for cpu resources again;

What is the difference between yield() and sleep()

  1. When the sleep method gives other threads the chance to run, it does not consider the priority of the thread, so it will give low priority threads the chance to run, while the yield method will only give threads with the same priority or higher priority the chance to run
  2. After the thread executes the sleep() method, it turns into the blocking state. Therefore, the thread executing the sleep() method will not be executed within the specified time, while the yield() method only returns the current thread to the executable state. Therefore, the thread executing the yield() method may be executed immediately after entering the executable state
  3. The sleep() method declares that an InterruptedException is thrown, while the yield() method declares no exception
  4. The sleep() method is more portable than the yield() method (related to the operating system)

3.4 thread join()

  • Merge threads. After the thread calling the join method is completed, execute other threads, and other threads are blocked;
  • Imagine jumping in line;
  • There is a checked exception InterruptedException in the join;

3.5 thread State

  • NEW: the thread that has not been started (i.e. start() has not been called) is in this state;
  • RUNNABLE: the running thread is in this state (the thread executing in the Java virtual machine);
  • BLOCKED: the thread BLOCKED waiting for the monitor to lock is in this state;
  • WAITING: the thread WAITING for another thread to perform a specific action is in this state;
  • TIMED_WAITING: the thread that is waiting for another thread to execute the action for the specified waiting time is in this state;
  • TERMINATED: the exited thread is in this state;

3.6 thread PRIORITY

Java provides a thread scheduler to monitor all threads that enter the ready state after starting in the program. The thread scheduler determines which thread should be scheduled to execute according to the priority (the actual scheduling is still scheduled by the cpu, and the priority is only recommended);

Thread priority is expressed in numbers, ranging from 1 to 10

  • Thread.MIN_PRIORITY = 1;
  • Thread.MAX_PRIORITY = 10;
  • ‚Äč Thread.NORM_PRIORITY = 5;

Use getPriority() or setPriority(int newPriority) to change or get priority

daemon thread 3.7

  • Threads are divided into user threads and guard threads;
  • The virtual machine must ensure that the user thread is executed;
  • The virtual machine does not have to wait for the daemon thread to finish executing, for example: gc;
  • Usage scenarios, such as recording operation logs in the background, monitoring memory, garbage collection, etc

Set whether it is a daemon thread through setDaemon(boolean on) (false by default, not a daemon thread);

4. Thread synchronization

4.1 introduction to synchronization

Since multiple threads of the same process share the same storage space, while multithreading brings convenience, it also brings the problem of access conflict. In order to ensure the accuracy of data in each thread, the lock mechanism synchronized is added during access. When a thread obtains the exclusive lock of the object and monopolizes resources, Other threads must wait for the occupying thread to release the lock before competing for resources.

But locks can also cause some problems:

  • A thread holding a lock will cause all other threads that need the lock to hang;
  • In multi thread competition, locking and releasing locks will lead to more context switching and scheduling delays, resulting in performance problems;
  • If a thread with high priority waits for a thread with low priority to release the lock, it will lead to priority inversion and performance problems;

4.2 synchronized keyword

Synchronized can modify methods or code blocks. When modifying ordinary methods, it locks the current object. When modifying static methods, it locks the entire class. When modifying code blocks, synchronized(this) {} locks the current object and synchronized(this.getClass()) locks the entire class.

4.3 thread deadlock

Multiple threads occupy some shared resources and wait for the resources occupied by other threads to run. As a result, two or more threads are waiting for each other to release resources and stop execution. When a synchronization block has "locks of more than two objects" at the same time, the "deadlock" problem may occur.

Four necessary conditions for deadlock generation:

  1. Mutually exclusive condition: a resource can only be used by one process at a time;
  2. Request and hold condition: when a process is blocked by requesting resources, it will hold on to the obtained resources;
  3. Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived before they are used up;
  4. Circular waiting conditions: a circular waiting resource relationship is formed between several processes;
//Deadlock example
public class _05ThreadDeadLock {
    static Object knife = new Object();
    static Object fruit = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (knife){
                System.out.println("Get a knife and want fruit");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (fruit){
                    System.out.println("Get fruit");
                }
            }
            /*
            //Solution: internal lock and external lock:
            synchronized (fruit){
                System.out.println("Obtain fruit "");
            }
            */
        }).start();
        new Thread(() -> {
            synchronized (fruit){
                System.out.println("Get fruit and want a knife");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (knife){
                    System.out.println("Get a knife");
                }
            }
            /*
            //Solution: internal lock and external lock:
            synchronized (knife){
                System.out.println("Obtain knife ");
            }
            */
        }).start();
    }
}
/*
The operation result is:
    Get a knife and want fruit
    Get fruit and want a knife
    The program cannot stop
*/

4.4 lock interface (lock)

  • From jdk5 Since 0, Java has provided a more powerful thread synchronization mechanism -- realizing synchronization by explicitly defining synchronization lock objects.
  • java.util.concurrent.locks.Lock interface is a tool that controls multiple threads to access shared resources. Locks provide exclusive access to shared resources. Only one thread can lock the lock object at a time. Threads should obtain the lock object before accessing shared resources.
  • ReentrantLock class implements Lock, which has the same concurrency and memory semantics as synchronized keyword. ReentrantLock (reusable Lock) is commonly used in thread safety control, which can display locking and releasing locks.

public class _05ThreadLock {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(new Ticket()).start();
        }
    }
}

class Ticket implements Runnable {

    private static int ticket = 10;
    private static final Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            lock.lock();
            try {
                if (ticket < 1)break;
                System.out.println("The first"+ticket--+"Zhang");
                Thread.sleep(500);
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                //When using a Lock, it is recommended to use the finally of the try catch statement to ensure the release of the Lock
                lock.unlock();
            }
        }
    }
}

Comparison between Lock and synchronized:

  • Lock is an explicit lock (you need to manually open and close the lock!), synchronized is an implicit lock that is automatically released out of scope.
  • Lock only has code block lock, and synchronized has code block lock and method lock.
  • Using Lock lock, the JVM will spend less time scheduling threads, with better performance and better scalability. (JUC provides different implementation classes)
  • Priority: Lock > sync code block > sync method.

5. Thread collaboration

Classic thread collaboration: producer consumer model.

Producer consumer model refers to that the producer produces a product and the consumer consumes a product. When there is already a product, the producer needs to be notified to stop production and wait for consumer consumption. When there is no product, the producer needs to be notified to continue production and the consumer needs to wait for product production.

Faced with this thread synchronization problem, producers and consumers share the same resource, and producers and consumers are interdependent and conditional on each other. At this time, only synchronized is not enough, because synchronized cannot realize message transmission (Communication) between different threads.

5.1 thread communication

Java provides several methods to solve the communication problem between threads:


Note: all methods of Object class can only be used in synchronization methods or synchronization code blocks, otherwise exceptions will be thrown
IllegalMonitorStateException.

The Lock interface needs to complete the inter thread communication through the Condition interface through Lock Newcondition() obtains the Condition and calls its await() and signalAll() methods. (similar to wait() and notifyAll())

5.2 code implementation of producer consumer mode

//Code implementation of producer consumer model
public class ThreadProCus {
    public static void main(String[] args) {
        Goods Goods = new Goods();
        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                lockGoods.add();
            }
        },"A").start();
        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                lockGoods.sub();
            }
        },"B").start();
    }
}

class Goods {
    private int num;
    //Lock mode implementation
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void add(){
        lock.lock();
        try {
            while (num == 10){
                //Product is full, stop production
                condition.await();
            }
            num++;
            System.out.println("Produced the second"+num+"Pieces of goods");
            //Produce products and inform consumers of consumption
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void sub(){
        lock.lock();
        try {
            while (num == 0){
                condition.await();
            }
            System.out.println("Consumed the second"+num+"Pieces of goods");
            num--;
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    /*
    //synchronized Mode implementation
    public synchronized void add() throws InterruptedException {
        while (num == 10){
            //Producer stop
            this.wait();
        }
        num++;
        System.out.println("Produce one, current num = "+ num");
        //Inform consumers
        this.notifyAll();
    }

    public synchronized void sub() throws InterruptedException {
        while (num == 0){
            //Consumer stop
            this.wait();
        }
        num--;
        System.out.println("Consume one, current num = "+ num");
        //Notify producer
        this.notifyAll();
    }
    */
}

6. Thread pool

Resources that are often created and destroyed and used heavily, such as threads in concurrency, have a great impact on performance.

At this time, in order to solve this problem, you can create multiple threads in advance, put them into the thread pool, obtain them directly during use, and put them back into the pool after use, so as to avoid frequent creation and destruction and realize reuse.

Advantages of thread pool:

  • Provides response speed (reduces time to create new threads)
  • Reduce resource consumption (reuse threads in the thread pool and do not need to be created every time)
  • Easy thread management

The properties that can be set for the thread pool include: corePoolSize (the size of the core pool, i.e. the number of resident threads), maximumPoolSize (the maximum number of threads that can be accommodated), keepAliveTime (the maximum survival time of a thread without a task), etc

From jdk5 Starting from 0, thread pool related API s are provided: ExecutorService and Executors.

ExecutorService: the real thread pool interface. Common subclass ThreadPoolExecutor

  • void execute(Runnable command): executes a task / command without a return value. It is generally used to execute Runnable
  • Future submit(Callable task): execute a task with a return value. It is generally used to execute Callable tasks
  • void shutdown(): closes the connection pool

Executors: tool class and factory class of thread pool, which are used to create and return different types of thread pools.

//Create threads from thread pool
public class _09ThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            executorService.execute(new MyThread());
        }
        executorService.shutdown();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

Wait, and

Learning suggestions

Many people worry that learning is easy to forget. Here is a way to teach you, that is, repeat learning.

last

Thank you very much for seeing here. It's not easy to be original. If you think this practical method is useful to you, please help. Some attention, praise and messages are all support (don't go whoring for nothing)! If you have any questions, please leave a message in the comment area!!!


I will regularly share 6-7 original articles on dry goods such as operating system, computer network, Java, distributed and database every week. See you in the next article!

Topics: Java jvm Programmer