The article directories

  • An overview of the
  • Thread class inheritance
  • Implement the Runnable interface
  • Implement Callable interface
  • The thread pool
  • Five states of threads
  • Multi-threaded ticket buying case
  • A deadlock
  • The Lock Lock
  • Producer-consumer problem
  • Eight lock problem
  • volatile
  • Write in the back

An overview of the

Interview, multithreading and concurrent programming is essential, I often see this kind of problem, also easy to understand at the time, what Thread class inheritance, realize the Runnable interface, said these are rotten, know these, of course, is not enough, so this several days a catch up with the search related information, for the convenience of the late review, make a summary here.

Thread class inheritance

The primitive approach is to inherit Thread and override the run method.

package com.hzy;

public class Main {
    public static void main(String[] args) {
        newMyThread().start(); }}class MyThread extends Thread {
    @Override
    public void run(a) {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":"+ i); }}}Copy the code

Because Java is single-inherited, this approach is rarely used, and the Runnable interface is generally used

Implement the Runnable interface

Several common constructors are presented here

First give a traditional method implementation

package com.hzy;

public class Main {
    public static void main(String[] args) {
        new Thread(new MyThread()).start();
        new Thread(new MyThread(),"He Zhiying").start(); }}class MyThread implements Runnable {
    @Override
    public void run(a) {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":"+ i); }}}Copy the code

We can also do this through anonymous inner classes

package com.hzy;

public class Main {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run(a) {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + ":"+ i); } } }).start(); }}Copy the code

It can also be implemented through the Lamata expression

package com.hzy;

public class Main {
    public static void main(String[] args) {
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + ":"+ i); } }).start(); }}Copy the code

Implement Callable interface

The Callable interface can receive a return value, can throw an exception, and override the Call method

package com.hzy;

import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // Create the execution service
        ExecutorService service = Executors.newFixedThreadPool(3);
        // Commit execution
        Future<Boolean> future1 = service.submit(new MyThread());
        Future<Boolean> future2 = service.submit(new MyThread());
        Future<Boolean> future3 = service.submit(new MyThread());
        // Get the return value
        Boolean b1 = future1.get();
        Boolean b2 = future2.get();
        Boolean b3 = future3.get();

        System.out.println(b1);
        System.out.println(b2);
        System.out.println(b3);
        // Shut down the serviceservice.shutdown(); }}class MyThread implements Callable<Boolean> {

    @Override
    public Boolean call(a) throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
        return true; }}Copy the code

It can also be created through the FutureTask adapter



package com.hzy;

import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // Create an adapter
        FutureTask futureTask = new FutureTask(new MyThread());
        new Thread(futureTask,"A").start(); Boolean o = (Boolean) futureTask.get(); System.out.println(o); }}class MyThread implements Callable<Boolean> {

    @Override
    public Boolean call(a) throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
        return true; }}Copy the code

The thread pool

Threads are created in much the same way as Callable, using the ExecutorService

package com.hzy;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args){

        // Create a cacheable thread pool. If the length of the pool exceeds the processing requirement, you can recycle idle threads, or create new threads if none are available.
        ExecutorService service1 = Executors.newCachedThreadPool();

        // Create a thread pool with a fixed length. The maximum number of concurrent threads can be controlled.
        ExecutorService service2 = Executors.newFixedThreadPool(10);

        // Create a thread pool of fixed length to support scheduled and periodic task execution.
        ExecutorService service3 = Executors.newScheduledThreadPool(10);

        // Create a single threaded thread pool that uses only one worker thread to execute tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority).
        ExecutorService service4 = Executors.newSingleThreadExecutor();

        / / execution
        service1.execute(new MyThread());
        service2.execute(new MyThread());
        service3.execute(new MyThread());
        service4.execute(new MyThread());
        
        // Close the connectionservice1.shutdown(); service2.shutdown(); service3.shutdown(); service4.shutdown(); }}class MyThread implements Runnable {

    @Override
    public void run(a) { System.out.println(Thread.currentThread().getName()); }}Copy the code

As specified in the Alibaba development manual, ThreadPoolExecutor is used to create thread pools

Learn from ThreadPoolExecutor with seven parameters and four rejection strategies

int corePoolSize// Core thread pool size
int maximumPoolSize// Maximum core thread pool size
long keepAliveTime// Timeout duration
TimeUnit unit// Timeout unit
BlockingQueue<Runnable> workQueue// block the queue
ThreadFactory threadFactory// Thread factory, used to create threads
RejectedExecutionHandler handler// Reject the policy
Copy the code

AbortPolicy());// If the bank is full and someone comes in, do not handle it, throw an exception (default)
CallerRunsPolicy();// When the bank is full, it is not processed
DiscardPolicy();// If the bank is full, throw the thread away without exception
DiscardOldestPolicy();// If the bank is full, it will compete with the thread that comes first
Copy the code
package com.hzy;

import java.util.concurrent.*;

public class Main {
    public static void main(String[] args){

        /** * use a bank example to illustrate these seven parameters */
        ExecutorService threadPool = new ThreadPoolExecutor(
                2.// Two open Windows
                5.// Five Windows, three of which are for emergency use
                3.// The inventory time expires
                TimeUnit.SECONDS,// Timeout time unit
                new LinkedBlockingDeque<>(3),// The size of the waiting area
                Executors.defaultThreadFactory(),// Default thread pool factory
                new ThreadPoolExecutor.AbortPolicy());// If the bank is full and someone comes in, do not handle it, throw an exception (default)
                // new ThreadPoolExecutor.CallerRunsPolicy(); // When the bank is full, it is not processed
                // new ThreadPoolExecutor.DiscardPolicy(); // If the bank is full, throw the thread away without exception
                // new ThreadPoolExecutor.DiscardOldestPolicy(); // If the bank is full, it will compete with the thread that comes first
        for (int i = 0; i < 8; i++) {
            threadPool.execute(()->{
                System.out.println(Thread.currentThread().getName());
            });
        }
        // Close the connectionthreadPool.shutdown(); }}Copy the code

When defining the maximum size of a thread pool, there are two strategies: CPU intensive and I/O intensive. In this case, CPU intensive is the number of CPU cores. Mine is eight cores, so it is 8. // Obtain the number of CPU cores, which is IO intensive, that is, determine how many programs are consuming IO threads, the maximum thread pool size should be greater than this value.

Five states of threads



Create a state of

The so-called creation state is oursnew Thread();

The ready state

By ready state, we aremyThread.start();

Running state

The running state is our code execution.

The blocking state

When a thread waits, synchronized, sleeps, and joins

State of death

The run() and main() ends

Common thread methods

  • setPriority(int new Priority)Changes the priority of a thread
  • sleep(long millis)Puts the current thread into sleep
  • join()It is equivalent to queue-jumping. After the execution of the inserted thread ends, the queue-jumping thread continues to execute.
  • yield()Thread courtesy suspends the current thread and transitions the thread state to the ready state
  • interrupt()Interrupt thread (not recommended)
  • isAlive()Checks whether the thread is alive

Multi-threaded ticket buying case

Thread-safety issues occur when multiple threads operate on the same resource. Let’s say we have 100 tickets sold in three Windows, so we have three threads to buy tickets.

package com.hzy;

public class Main {
    public static void main(String[] args){
        MyThread myThread = new MyThread();
        new Thread(myThread,"Window 1").start();
        new Thread(myThread,"Window 2").start();
        new Thread(myThread,"Window 3").start();
        new Thread(myThread,4 "window").start();
        new Thread(myThread,5 "window").start(); }}class MyThread implements Runnable {
    private int ticket = 100;
    private boolean flag = true;
    @Override
    public void run(a) {
        while(flag) { buy(); }}public void buy(a) {
        if (ticket <= 0) {
            flag = false;
            return;
        }
        try {
            Thread.sleep(100);// Simulate ticket delay of 100ms
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":"+ ticket--); }}Copy the code

As you can see, there is a thread-safety issue because multiple threads are coming in at the same time before ticket goes to 0. Synchronized thread can be synchronized, you can use synchronized methods and synchronized code blocks to solve. Synchronized methods, that is, add the synchronized keyword to a method.

package com.hzy;

public class Main {
    public static void main(String[] args){
        MyThread myThread = new MyThread();
        new Thread(myThread,"Window 1").start();
        new Thread(myThread,"Window 2").start();
        new Thread(myThread,"Window 3").start(); }}class MyThread implements Runnable {
    private int ticket = 100;
    private boolean flag = true;
    @Override
    public void run(a) {
        while(flag) { buy(); }}public synchronized void buy(a) {
        if (ticket <= 0) {
            flag = false;
            return;
        }
        try {
            Thread.sleep(100);// Simulate ticket delay of 100ms
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":"+ ticket--); }}Copy the code

A synchronized code block locks an object, that is, the amount that needs to change

package com.hzy;

public class Main {
    public static void main(String[] args){
        MyThread myThread = new MyThread();
        new Thread(myThread,"Window 1").start();
        new Thread(myThread,"Window 2").start();
        new Thread(myThread,"Window 3").start(); }}class MyTicket {
    int ticket;

    public MyTicket(int ticket) {
        this.ticket = ticket; }}class MyThread implements Runnable {
    MyTicket myTicket = new MyTicket(100);
    private boolean flag = true;
    @Override
    public void run(a) {
        while(flag) { buy(); }}public void buy(a) {
        synchronized (myTicket) {
            if (myTicket.ticket <= 0) {
                flag = false;
                return;
            }
            try {
                Thread.sleep(100);// Simulate ticket delay of 100ms
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":"+ myTicket.ticket--); }}}Copy the code

A deadlock

A wants B’s resources when A has A resource, and B wants A’s resources when B has A resource.

package com.hzy;

public class Main {
    public static void main(String[] args){
        A a = new A();
        B b = new B();
        new Thread(new MyThread(a,b,0)).start();
        new Thread(new MyThread(a,b,1)).start(); }}class A {}class B {}class MyThread implements Runnable {
    private A a;
    private B b;
    private int choice;
    public MyThread(A a,B b,int choice) {
        this.a = a;
        this.b = b;
        this.choice = choice;
    }
    @Override
    public void run(a) {
        if (choice == 0) {
            synchronized (a) {
                System.out.println(Thread.currentThread().getName() + "Get" + a);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b) {
                    System.out.println(Thread.currentThread().getName() + "Get"+ b); }}}else {
            synchronized (b) {
                System.out.println(Thread.currentThread().getName() + "Get" + b);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a) {
                    System.out.println(Thread.currentThread().getName() + "Get" + a);
                }
            }
        }
    }
}
Copy the code

As can be seen, A needs B resources, B needs A resource, deadlock state appears.

The Lock Lock

Lock is an interface, not a key word, he is a display locks, Lock can only be synchronized code block, can’t Lock method, can display the Lock, the Lock is released, you can specify a wake up one thread, the Lock Lock has an implementation class is already, reentrant Lock Lock (recursion), said here, all the locks are reentrant Lock, If we acquire the lock on the outside, we will automatically acquire the lock on the inside.

Normally, if we do not lock, there will be thread safety issues

package com.hzy;

public class Main {
    public static void main(String[] args){
        MyThread myThread = new MyThread();
        new Thread(myThread).start();
        new Thread(myThread).start();
        newThread(myThread).start(); }}class MyThread implements Runnable {

    private int ticket = 100;
    @Override
    public void run(a) {
        while (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch(InterruptedException e) { e.printStackTrace(); } System.out.println(ticket --); }}}Copy the code

RenntrantLock can be used to lock, show the lock, remember to unlock.

package com.hzy;

import java.util.concurrent.locks.ReentrantLock;

public class Main {
    public static void main(String[] args){
        MyThread myThread = new MyThread();
        new Thread(myThread).start();
        new Thread(myThread).start();
        newThread(myThread).start(); }}class MyThread implements Runnable {

    private int ticket = 100;
    // Define lock lock
    ReentrantLock lock = new ReentrantLock();
    @Override
    public void run(a) {
        while (true) {
            lock.lock();
            if (ticket > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(ticket --);
            }else {
                break; } lock.unlock(); }}}Copy the code

A finer lock than ReentrantLock is ReentrantReadWriteLock, which has a read lock and a write lock

package com.hzy;

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        MyReadWriteLock myReadWriteLock = new MyReadWriteLock();
        for (int i = 0; i < 5; i++) {
            int temp = i;
            new Thread(()->{
                myReadWriteLock.put(temp + "",temp + "");
            },temp + "").start();
        }

        for (int i = 0; i < 5; i++) {
            int temp = i;
            new Thread(()->{
                myReadWriteLock.get(temp + "");
            },temp + "").start(); }}}class MyReadWriteLock {
    private volatile Map<String,String> map = new HashMap<>();

    public void put(String key,String value) {
        System.out.println(Thread.currentThread().getName() + "Written" + key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName() + "Write complete");
    }

    public void get(String key) {
        System.out.println(Thread.currentThread().getName() + "Read" + key);
        map.get(key);
        System.out.println(Thread.currentThread().getName() + "Read completed"); }}Copy the code

If it is not locked, it will be inserted by another thread before the write is complete

What we want is that when we write, it can only be one thread, and when we read, it can be multiple threads.

package com.hzy;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Main {
    public static void main(String[] args) {
        MyReadWriteLock myReadWriteLock = new MyReadWriteLock();
        for (int i = 0; i < 5; i++) {
            int temp = i;
            new Thread(()->{
                myReadWriteLock.put(temp + "",temp + "");
            },temp + "").start();
        }

        for (int i = 0; i < 5; i++) {
            int temp = i;
            new Thread(()->{
                myReadWriteLock.get(temp + "");
            },temp + "").start(); }}}class MyReadWriteLock {
    private volatile Map<String,String> map = new HashMap<>();
    ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    public void put(String key,String value) {
        readWriteLock.writeLock().lock();
        System.out.println(Thread.currentThread().getName() + "Written" + key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName() + "Write complete");
        readWriteLock.writeLock().unlock();
    }

    public void get(String key) {
        readWriteLock.readLock().lock();
        System.out.println(Thread.currentThread().getName() + "Read" + key);
        map.get(key);
        System.out.println(Thread.currentThread().getName() + "Read completed"); readWriteLock.readLock().unlock(); }}Copy the code

As you can see, when writing, it’s one write, one write completes, and when reading, there may be multiple reads.



The difference between synchronized and Lock

  • Synchronized is the keyword and Lock is the class
  • Synchronized cannot obtain the status of a Lock, whereas Lock can
  • Synchronized automatically releases the Lock. You need to manually release the Lock
  • Synchronized is not as flexible as Lock.

Theories don’t work, so here are a few ReentrantLock scenarios

  • ReentrantLock is unfair by default, but it can set a fair lock, that is, whoever comes first gets the lock firstnew ReentrantLock(true).
  • ReentrantLock can respond to interrupts, that is, when two threads are deadlocked, you interrupt thread A and thread B can run normally.
  • ReentrantLock can solve deadlock problems by implementing time-limited wait with tryLock().

synchronized

  • Synchronized optimizes locking in JDK1.6, that is, when a thread accesses a synchronized code block for many times, the threadId of the thread will be recorded, that is, when you visit again, I only need to determine threadId, which is highly efficient and belongs to biased locking.
  • When there are multiple threads coming, the lock is upgraded to a lightweight lock, that is, to try to acquire the lock through the CAS, which is a spin lock state. If the lock can be obtained in a short period of time, it does not block and saves time for CUP context switching.
  • If a lock is not acquired for a long time, it consumes CPU resources because there is an endless loop, and after a period of time it will upgrade to a heavyweight lock and block. The lock upgrade is irreversible.

Producer-consumer problem

So this is a classic thread communication problem, where there’s a connection between different threads, producers produce something and put it in the buffer, if the buffer is full, producers block, the buffer is empty, consumers block.

Common methods are wait(), notify(), and notifyAll().

package com.hzy;

import java.util.concurrent.locks.ReentrantLock;

public class Main {
    public static void main(String[] args){
        Buffer buffer = new Buffer();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println("The producer produces."+ i); buffer.put(); }},"Producer").start();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println("Consumers spend."+ i); buffer.get(); }},"Consumer").start(); }}/ / the buffer
class Buffer {
    private int len = 0;
    public synchronized void put(a) {
        if (len < 10) {
            len ++;
        } else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        notifyAll();
    }
    public synchronized void get(a) {
        if (len > 0) {
            len --;
        } else {
            try {
                wait();
            } catch(InterruptedException e) { e.printStackTrace(); } } notifyAll(); }}Copy the code

The producer-consumer problem can be implemented with Lock Lock. You can specify to wake up a thread, and the common methods are await, signal. So this is the buffer

/ / the buffer
class Buffer {
    private int len = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();// This object can set some conditions
    public void put(a) {
        lock.lock();
        if (len < 10) {
            len ++;
        } else {
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        condition.signalAll();
        lock.unlock();
    }
    public void get(a) {
        lock.lock();
        if (len > 0) {
            len --;
        } else {
            try {
                condition.await();
            } catch(InterruptedException e) { e.printStackTrace(); } } condition.signalAll(); lock.unlock(); }}Copy the code

Condition is introduced here, which can specify a thread to wake up. Here we demonstrate three abCs.

package com.hzy;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
    public static void main(String[] args){
        Buffer buffer = new Buffer();
        new Thread(()->{
            for (int i = 0; i < 10; i++) { buffer.a(); }},"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) { buffer.b(); }},"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) { buffer.c(); }},"C").start(); }}/ / the buffer
class Buffer {
    private int flag = 1;// 1 Go to A. 2 Go to B. 3 Go to C
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();// This object can set some conditions
    Condition condition2 = lock.newCondition();// This object can set some conditions
    Condition condition3 = lock.newCondition();// This object can set some conditions
    public void a(a) {
        lock.lock();
        try {
            while(flag ! =1) {
                condition1.await();
            }
            System.out.println("A");
            flag = 2;
            condition2.signal();// specify to wake up B
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}public void b(a) {
        lock.lock();
        try {
            while(flag ! =2) {
                condition2.await();
            }
            System.out.println("B");
            flag = 3;
            condition3.signal();// Specify wake up C
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}public void c(a) {
        lock.lock();
        try {
            while(flag ! =3) {
                condition3.await();
            }
            System.out.println("C");
            flag = 1;
            condition1.signal();// specify wake up A
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}}Copy the code

It’s all ABC, ABC…

Eight lock problem

one

package com.hzy;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args){
        Phone phone = new Phone();
        new Thread(()->{
            phone.sendMsg();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        }, "B").start(); }}class Phone {
    public synchronized void sendMsg(a) {
        System.out.println("sendMsg");
    }

    public synchronized void call(a) {
        System.out.println("call"); }}Copy the code

Print sendMsg first, not because sendMsg was called first, but because A line got the lock first.



two

package com.hzy;


import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args){
        Phone phone = new Phone();
        new Thread(()->{
            phone.sendMsg();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        }, "B").start(); }}class Phone {
    public synchronized void sendMsg(a) {
        try {
            TimeUnit.SECONDS.sleep(3);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMsg");
    }

    public synchronized void call(a) {
        System.out.println("call"); }}Copy the code

In this example, the synchronized lock is obtained by sendMsg first, and the object of the synchronized lock is the caller. These two methods use the same lock, and the one who obtains the lock first is the one who executes it first



three

package com.hzy;


import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args){
        Phone phone = new Phone();
        new Thread(()->{
            phone.sendMsg();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        }, "B").start(); }}class Phone {
    public synchronized void sendMsg(a) {
        try {
            TimeUnit.SECONDS.sleep(3);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMsg");
    }

// public synchronized void call() {
// System.out.println("call");
/ /}
        public void call(a) {
        System.out.println("call"); }}Copy the code

Synchronized (” call “, “synchronized”, “call”, “call”)



four

package com.hzy;


import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args){
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
            phone1.sendMsg();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        }, "B").start(); }}class Phone {
    public synchronized void sendMsg(a) {
        try {
            TimeUnit.SECONDS.sleep(3);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMsg");
    }

    public synchronized void call(a) {
        System.out.println("call"); }}Copy the code

Which of the two phone objects is called first, sendMsg and call? The answer is call first, because the lock here is on the object, and they are not the same object, so the resource is not affected.



five

package com.hzy;


import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args){
        Phone phone = new Phone();
        new Thread(()->{
            phone.sendMsg();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        }, "B").start(); }}class Phone {
    public static synchronized void sendMsg(a) {
        try {
            TimeUnit.SECONDS.sleep(3);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMsg");
    }

    public static synchronized void call(a) {
        System.out.println("call"); }}Copy the code

Call the static synchronization method with an object to see if sendMsg or call comes first. The answer is sentMsg because it locks the Class object (only one).



six

package com.hzy;


import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args){
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
            phone1.sendMsg();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        }, "B").start(); }}class Phone {
    public static synchronized void sendMsg(a) {
        try {
            TimeUnit.SECONDS.sleep(3);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMsg");
    }

    public static synchronized void call(a) {
        System.out.println("call"); }}Copy the code

Phone1 and phone2 belong to the Class object of Phone.



seven


package com.hzy;


import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args){
        Phone phone = new Phone();
        new Thread(()->{
            phone.sendMsg();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        }, "B").start(); }}class Phone {
    public static synchronized void sendMsg(a) {
        try {
            TimeUnit.SECONDS.sleep(3);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMsg");
    }

    public synchronized void call(a) {
        System.out.println("call"); }}Copy the code

So we’re locking a static synchronized method and a normal synchronized method, and we’re calling it on an object, and what do we get? The answer is call first, because a static synchronized method locks the Class object, whereas a normal synchronized method locks the caller, not the same thing.



eight

package com.hzy;

import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args){
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
            phone1.sendMsg();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        }, "B").start(); }}class Phone {
    public static synchronized void sendMsg(a) {
        try {
            TimeUnit.SECONDS.sleep(3);// Rest for a second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMsg");
    }

    public synchronized void call(a) {
        System.out.println("call"); }}Copy the code

Call (sendMsg) and call (call). Which one will be printed first? The answer is call, because sendMsg locks the Class and call locks the caller, not the same thing

volatile

Speaking of volatile, it’s not hard to think of three features that guarantee visibility, not atomicity, and prohibit instruction reordering. Before we talk about volatile, we need to talk about the Java Memory Model (JMM). When a thread reads a variable in Memory, it copies that variable into the CPU’s cache, operates on it, and writes that variable to Memory. In single thread won’t appear any problems, but there will be problems in a multithreaded, when a = 1: thread one reads the variables to the cache area, add 1 operation hasn’t written to memory, thread 2 read the memory variables in a + 1 = 1 is for operation, and then read: thread one writes a = 2, thread 2 also write a = 2 to memory, so in the end, The value of this variable is 2, not 3 (thread-safety issues arise). We want to be when the thread 1 plus 1, let the thread 2 know, this is the role of volatile, can guarantee the visibility, that is, when a thread 1 plus 1 for a variable operation, will directly written to the memory (immediately) immediately, and notify the thread 2, variables are modified, thread 2 the value of the buffer memory to read. However, add 1 operation is not atomic (three steps, first reads a variable’s value, and then add 1 to the operation, and then assigned to a), that is to say, when a thread reads a variable to the buffer 1, has yet to modify a value, thread 2 came in at this time, read a value, and to add 1 operation, because of the visibility, However, the CPU in thread 1 has read the value of the buffer before it was updated, so there is a thread-safe issue, and also a volatile issue that does not guarantee atomicity. You then need the increments to be atomic, and you have an automic package with methods that allow increments to be atomic (and other atomic operations).

The autoincrement in atomic operations, in fact, is using spin locks, which are always comparing and swapping.

If we want to change the value of num, we can use a method called compareAndSet(5,6), which means we expect num to be 5, And if it is, we change it to 6. But there is a problem, we expect num to be 5, if some other thread changes 5 to 8, then to 5 again, and finally to 5, but it has already been changed once, that is an ABA problem. An AtomicStampedReference is created with an imprint, which is the version number, and is updated each time an ABA problem occurs, so you know exactly how many times an ABA problem has been modified.

Another feature is to prohibit instruction rearrangement. If you want to prohibit it, you have to know what instruction rearrangement is. The so-called instruction rearrangement, that is, in order to improve the efficiency of the program, the optimization of the register to the code, if we have a code as follows

a = 1;/ / 1
b = 2;/ / 2
a = a + 1;/ / 3
b = a + 1;/ / 4
Copy the code

The so-called instruction reordering is the optimal execution ordering without affecting the results of a single-threaded program. It can be seen that the execution order of statement 1 and statement 2 does not affect the results of the program. The final value of A is 2, and the value of B is 3. Instance Instance = new Instance(); From the microcosmic point of view, this statement can be broken down into three steps, one is allocated memory space, 2 it is to initialize the object 3 it is to point to the memory space, the second and third order rearrangement will happen, if in the case of multiple threads, thread A allocated memory space, and execute the memory space (uninitialized object), the thread B to access the memory space, This is going to go wrong. Volatile disallows instruction reordering, but not absolutely, assuming we have 1, 2, 3, 4, 5 statements

a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
Copy the code

If we volatile c, a and B would still be rearranged, D and E would be rearranged, ab and DE would not be rearranged, and C would separate them (the technical term is to add a memory barrier to C).

Write in the back

Hehe learning programming, focusing on Java backend, interview questions, data structure, algorithm, database, Linux and other programming knowledge, looking forward to your attention.