1. Basic Concepts

1.1, processes,

Processes are the smallest unit of system resource allocation. It consists of text area, data area and stack.

  • The text area stores the code executed by the processor
  • The data area stores variables and dynamically allocated memory used during process execution;
  • The stack area stores the instructions and local variables of the active procedure call.

Issues involved: CPU preemption, memory allocation (virtual/physical memory), and interprocess communication.

1.2, thread

A thread is the smallest unit in which an operating system can schedule operations.

A process can consist of multiple threads that share the resource space allocated to the process

Issues involved: thread state, concurrency issues, locks

1.3, coroutines

Subroutine: part of the code of a main program, i.e. a method or function.

Wikipedia: Execution is like a subroutine, with its own context, but with its own control over switching.

1.4 frequently Asked Questions

  • 1. The difference between processes and threads
Processes have their own resource space, and threads need to rely on the process to allocate resources to perform corresponding tasks. Interprocess communication relies on pipes, shared memory, signals, and message queues. The thread is not safe, easy to cause the process crashCopy the code
  • 2. What is multithreading
Thread is the smallest unit of scheduling, that is, each processor can handle only one thread task at a point in time. On multi-core CPU, in order to improve our CPU utilization rate, thus led to the implementation of multi-threading. The concurrent execution of tasks is realized through concurrent scheduling of tasks in multiple threads. This is what we call multithreaded task execution.Copy the code

Second, the Thread

2.1. Use multi-threading

2.1.1. Inherit Thread

class JayThread extends Thread{
    @Override
    public void run(a){
        System.out.println("hello world in JayThread!"); }}class Main{
    public static void main(String[] args){
        JayThread t1 = newJayThread(); t1.start(); }}Copy the code

2.1.2. Implement Runnable interface

class JayRunnable implements Runnable{
    
    @Override
    public void run(){
        System.out.println("hello world in JayRunnable!") } } class Main{ public static void main(String[] args){ JayRunnable runnable = new JayRunnable(); Thread t1 = new Thread(runnable); t1.start(); }}Copy the code

2.1.3. Implement Callable interface

class JayCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("run in JayCallable " + Thread.currentThread().getName());
        return "Jayce";
    }
}


class Main{
    public static void main(String[] args) {
         Thread.currentThread().setName("main thread"); ThreadPoolExecutor executor =new ThreadPoolExecutor(10,20,60, timeUnit. SECONDS,new ArrayBlockingQueue<>(10)); Future<String> future = executor.submit(new JayCallable()); try { future.get(10, TimeUnit.SECONDS); }catch (Exception e){ System.out.println("Task execution timed out"); }}}Copy the code

2.1.4 Common Problems

  • 1. What are the ways to use multithreading

There is only one way to create a Java Thread, and that is by creating a new Thread().

  • 2,Thread().Runnable()Callable()The difference between

Thread needs to inherit, overriding the run() method, which is not extension-friendly, and a class is a Thread task.

Runnbale implements multiple interfaces as interfaces, inheriting from parent classes. You need to create a thread to perform the load task.

Runnable cannot return a result or throw an exception. Need to be used in conjunction with ThreadPoolExecutor.

  • 3,Thread.run()Thread.start()The difference between

Thread.run()

    public static void main(String[] args){
        Thread.currentThread().setName("main thread");
        Thread t1 = new Thread(()->{
            System.out.println("run in "+Thread.currentThread().getName());
        });
        t1.setName("Jayce Thread");
        t1.run();
    }
Copy the code

Output result:

Thread.start()

    public static void main(String[] args){
        Thread.currentThread().setName("main thread");
        Thread t1 = new Thread(()->{
            System.out.println("run in "+Thread.currentThread().getName());
        });
        t1.setName("Jayce Thread");
        t1.start();
    }
Copy the code

Output result:

The start() method starts the thread, puts the current task into the CPU waiting queue (enters the ready state, waits for the CPU fragment), and executes the run method after the fragment is obtained. The run() method execution, which is resolved as a call to a normal method, is executed directly in the current thread.Copy the code

2.2. Thread status

The thread state, also known as the life cycle of a thread, can be divided into five phases: new, ready, running, dead, and blocked.

Picture quote taro source code

2.2.1 new

The new state is better understood as the Thread class created when we call new Thread().

2.2.2 ready

Ready state refers to:

1. When thread. start is called, threads can start executing, but wait for CPU resources. Unlike thread. run, the run method executes directly on the current Thread, using its CPU resources.

2. In the running state, after CPU resources are used up, the system enters the ready state and waits to obtain CPU resources again. As you can see from the figure, thread.yield can be called directly to abandon the current CPU resource and enter the ready state. Let other, higher-priority tasks take precedence.

Then run

In the ready state of Step 2, after obtaining CPU resources, it enters the running state and executes the corresponding task, that is, the run() method we have implemented.

2.2.4 end

1. Normal tasks are completed, and the run() method is completed

2, exception exit, the program throws an exception, no capture

2.2.5 blocking

Blocking is mainly divided into IO wait, lock wait, and thread wait modes. Through the above picture can be seen intuitively.

I/O wait: Waits for user input, allocates CPU resources, and enters the ready state again after the operation is complete (I/O ready).

Lock wait: Synchronized code blocks need to wait to acquire locks before they can enter the ready state

Thread waiting: the sleep(), JOIN (), and wait()/notify() methods all block while waiting for the thread state.

Thread pools

2.1 Pooling technology

Pooling technology is a technical solution to reduce the loss caused by the creation and destruction of resources each time, and to improve the utilization rate of resources through the reuse of resources. Common examples are database connection pools, HTTP connection pools and thread pools. They are managed by the same pool and reused to improve resource utilization.

Benefits of using thread pools:

  • Reduced resource consumption: Reduces thread creation and destruction costs by reusing created threads.
  • Improved response time: When a task arrives, it can be executed immediately without waiting for the thread to be created.
  • Improve manageability of threads: Threads are scarce resources. If created without limit, they will not only consume system resources, but also reduce system stability. Thread pools can be used for uniform allocation, tuning, and monitoring.

2.2 Creating a thread pool

2.2.1 Executors (Not recommended)

Create thread pools like FixedThreadPool and CachedThreadPool quickly by following Executors.

Public static ExecutorService newSingleThreadExecutor(); // Create a single thread pool. Public static ExecutorService newFixedThreadPool(int nThreads); ExecutorService newCachedThreadPool(); ExecutorService newCachedThreadPool(); Public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize); ExecutorService newWorkStealingPool(); ExecutorService newWorkStealingPool();Copy the code

Existing disadvantages:

FixedThreadPool and SingleThreadExecutor: Allows requests with a queue length of Integer.MAX_VALUE, which can pile up requests to OOM. CachedThreadPool and ScheduledThreadPool: The number of threads allowed to be created is integer. MAX_VALUE, which may create a large number of threads, resulting in OOM.Copy the code

2.2.2 ThreadPoolExecuotr

Constructor:

       public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
Copy the code

Several core parameters:

  • 1.corePoolSize: Number of core threads
  • 2,maximumPoolSize: Maximum number of threads
  • 3,keepAliveTime: Indicates the idle lifetime of a thread
  • 4,unit: Time unit
  • 5,workQueue: Waiting queue
  • 6,threadFactory: Thread factory
  • 7,handler: Rejection strategy

With the ExecutorService. NewSingleThreadExecutor etc, comparing the multiple API can be relatively easy to identify the underlying implementation is dependent on the different definition of BlockingQueue thread pool.

There are several types of blocking queues:

  • 1.ArrayBlockingQueueQueues are bounded, array-based blocking queues
  • 2,LinkedBlockingQueueQueues can be bounded or unbounded. Blocking queues based on linked lists correspond to:Executors.newFixedThreadPool()The implementation of the.
  • 3,SynchronousQueueEach insert operation must wait until another thread calls the remove operation, otherwise the insert operation will remain blocked. Corresponding to the:Executors.newCachedThreadPool()The implementation of the.
  • 4,PriorityBlockingQueue, an unbounded blocking queue with priority

There are four rejection strategies:

  • 1.CallerRunsPolicy: executes on the caller thread
  • 2,AbortPolicy: direct throw RejectedExecutionException anomalies
  • 3,DiscardPolicy: The task is discarded without any processing
  • 4,DiscardOldestPolicy: Discards the oldest task in the queue and tries to execute the current task

2.3 Thread Pool Task submission

There are two main methods for submitting tasks to the thread pool, execute() and submit().

1, the execute ()

If no result is returned, the task is executed directly

public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    executor.execute(() -> System.out.println("hello"));
}
Copy the code

2, submit ()

Submit () returns a Future object, which is used to retrieve the returned result. Commonly used apis include GET () and GET (timeout,unit)

public static void main(String[] args) throws Exception {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    Future<String> future = executor.submit(() -> {
        System.out.println("hello world! ");
        return "hello world!";
    });
    System.out.println("get result: " + future.get());
}
Copy the code

Third, threading tools

3.1 ThreadlLocal

ThreadLocal, in many places it’s called thread-local variables, in some places it’s called thread-local storage, which actually means the same thing. As many of you probably know, ThreadLocal creates a copy of a variable in each thread, so each thread can access its own internal copy of the variable.

3.2 a Semaphore

Semaphore, a new synchronization class, is a count signal. Use sample code:

// Thread pool ExecutorServiceexec= Executors.newCachedThreadPool(); // Only 5 threads can access final Semaphore semp = new Semaphore(5); // Simulate 20 client accessesfor (int index = 0; index < 50; index++) {
            final int NO = index;
            Runnable run = new Runnable() {
                public void run() {try {// acquire semp.acquire(); System.out.println("Accessing: "+ NO); Thread.sleep((long) (Math.random() * 6000)); Semp.release (); AvailablePermits () refers to the number of permits in the current semaphore.out.println ()."-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"+ semp.availablePermits()); } catch (InterruptedException e) { e.printStackTrace(); }}}; exec.execute(run); } // exit the thread pool exec.shutdown();Copy the code

3.3 CountDownLatch

It can be interpreted as a fence that needs to wait for all threads to complete before continuing down.

The default constructor for CountDownLatch is CountDownLatch(int count), whose parameter indicates the number of counts that need to be reduced. The main thread calls the #await() method to tell CountDownLatch to block waiting for the specified number of counts to be reduced. Other threads then call CountDownLatch’s #countDown() method to reduce the count (without blocking). The wait count is reduced to zero, and the main thread ends the blocking wait and continues.

3.4 CyclicBarrier

Cyclicbarriers are similar to countdownlatches in that they allow threads to reach a certain point before they can continue, except that cyclicbarriers can be used multiple times. Sample code:

  
        CyclicBarrier barrier;
        
        public TaskThread(CyclicBarrier barrier) {
            this.barrier = barrier;
        }
        
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                System.out.println(getName() + "Reach fence A");
                barrier.await();
                System.out.println(getName() + "Break the fence A");
                
                Thread.sleep(2000);
                System.out.println(getName() + "Reach fence B");
                barrier.await();
                System.out.println(getName() + "Break the fence B"); } catch (Exception e) { e.printStackTrace(); }}Copy the code

Four,

Finally, I will post a new public account (Java tutorial), welcome your attention, mainly share the interview content (refer to the previous blogger’s article), ali’s open source technology and other related to Ali life. If you want to exchange interview experience, you can add my personal wechat (JAYCE-K) into the group to learn ~