Java Advanced --- Multithreading

Java Advanced-Multithreading

One, the concept of threads

Before we talk about threads, then we have to bring up the process again and again. (This word is too much, I don’t seem to recognize it)

process:

One-time execution of the program.

Thread:

The smallest unit that the operating system can perform operation scheduling. It is included in the process and is the actual operating unit in the process.

Second, the realization and method of thread

Thread thread = new Thread("线程1");//线程创建
System.out.println(thread.getName());//获取线程名称
thread.start();			//线程启动
thread.setPriority(8);  //设置优先级 1~10
thread.getState();		//获取线程状态
thread.isAlive();		//查看线程是否运行

Third, the realization of multi-threading

Ways to implement multithreading:
1. Inherit the Thread class
2. Implement the Runnable interface
3. Implement the Callable interface
4. Thread pool

1. Inherit the Thread class and
rewrite the run method

public class MyThread extends Thread{
    public void run(){      //线程运行的主体
        for (int i = 1; i <= 20; i++) {
            System.out.println(i+".你好,来自线程"+Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        MyThread thread2 = new MyThread();

        thread.start();     //真正意义上的多线程,因为run还是main主线程调用
        thread2.start();

    }
}

2. Implement the Runnable interface and
rewrite the run method

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 20; i++) {
            System.out.println(i+".你好,来自线程"+Thread.currentThread().getName());
            Thread.yield();     //礼让:放弃本次就会,重写竞争
        }
    }

    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread thread = new Thread(mr,"子线程1");
        Thread thread2 = new Thread(mr,"子线程2");
        thread.setPriority(1);      //设置优先级
        thread.setPriority(10);

        thread.start();
        thread2.start();
    }
}

3. Implement the Callable interface and
rewrite the call method.
Since the run method has no return value, you need to use the call method to return the return value to the client program.

Steps to implement multithreading using the Callable interface
1. The first step: create an instantiated object of the Callable subclass

2. Step 2: Create the FutureTask object and pass the Callable object into the FutureTask construction method (note: FutureTask implements the Runnable interface and the Future interface)

3. The third step: instantiate the Thread object and pass in the FurureTask object in the constructor

4. Step 4: Start the thread

FutureTask ft = new FutureTask (implementation class of Callable interface)
Thread thread = new Thread(ft
);
thread.start(); ft.get();//Get the return value of call() method

public class MyCallable implements Callable {
    @Override
    public String call() throws Exception {
        System.out.println("执行call方法");
        return "999";
    }

    public static void main(String[] args) {
        FutureTask<String> ft = new FutureTask(new MyCallable());
        Thread thread1 = new Thread(ft);
        thread1.start();
    }
}

4, thread pool
1. newFixedThreadPool
fixed-size thread pool, you can specify the size of the thread pool, the thread pool corePoolSize and maximumPoolSize are equal, the blocking queue uses LinkedBlockingQueue, the size is the maximum integer.

The number of threads in the thread pool is always the same. When a new task is submitted, an idle thread in the thread pool will be executed immediately, if not, it will be temporarily stored in the blocking queue. For a fixed-size thread pool, there is no change in the number of threads. At the same time, the unbounded LinkedBlockingQueue is used to store the executed tasks. When tasks are submitted very frequently, LinkedBlockingQueue

Rapid increase, there is a problem of exhausting system resources. And when the thread pool is idle, that is, when there is no runnable task in the thread pool, it will not release the worker thread, and will occupy certain system resources, requiring shutdown.

public class FixPoolDemo {

    private static Runnable getThread(final int i) {
        return new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
        };
    }

    public static void main(String args[]) {
        ExecutorService fixPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            fixPool.execute(getThread(i));
        }
        fixPool.shutdown();
    }
}

2. newSingleThreadExecutor
single thread thread pool, there is only one thread thread pool, the blocking queue uses LinkedBlockingQueue, if extra tasks are submitted to the thread pool, they will be temporarily stored in the blocking queue, and then executed when they are free. The tasks are executed in a first-in, first-out order.

public class CachePool {
    private static Runnable getThread(final int i){
        return new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                }catch (Exception e){

                }
                System.out.println(i);
            }
        };
    }

    public static  void main(String args[]){
        ExecutorService cachePool = Executors.newCachedThreadPool();
        for (int i=1;i<=10;i++){
            cachePool.execute(getThread(i));
        }
    }
}

3. newCachedThreadPool
cache thread pool, the cached thread survives for 60 seconds by default. The size of the core pool corePoolSize of the thread is 0, the maximum core pool is Integer.MAX_VALUE, and the blocking queue uses SynchronousQueue. It is a blocking queue submitted directly, and it will always force the thread pool to add new threads to perform new tasks. When there is no task execution, when the idle time of the thread exceeds keepAliveTime (60 seconds), the worker thread will be terminated and recycled. When a new task is submitted, if there is no idle thread, a new thread will be created to perform the task, which will cause a certain system Overhead. If a large number of tasks are submitted at the same time, and the task execution time is not particularly fast, then the thread pool will add an equal amount of thread pool processing tasks, which is likely to quickly exhaust system resources.

public class SingPoolDemo {
    private static Runnable getThread(final int i){
        return new Runnable() {
            @Override
            public void run() {
                try {

                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
        };
    }

    public static void main(String args[]) throws InterruptedException {
        ExecutorService singPool = Executors.newSingleThreadExecutor();
        for (int i=0;i<10;i++){
            singPool.execute(getThread(i));
        }
        singPool.shutdown();
    }

4. newScheduledThreadPool
timing thread pool, the thread pool can be used to perform tasks periodically, usually for periodic synchronization of data.
scheduleAtFixedRate: The task is executed at a fixed frequency, and the period refers to the interval between the successful execution of each task.
schedultWithFixedDelay: The task is executed with a fixed delay. The delay refers to the time after the last execution is successful and before the next execution starts.

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

        ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
        ses.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(4000);
                    System.out.println(Thread.currentThread().getId() + "执行了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 0, 2, TimeUnit.SECONDS);
    }
}

Three, synchronization mechanism

synchronized

for example:

Buying tickets:
asynchronous

public class BuyTicket extends Thread{
    int left = 15;  //总共15张票,余票
    int used = 0;   //已卖票数

    public synchronized void buy(){
        
    }
    public void run(){
        //取票
        while (left>0){
            left--;
            used++;
            try {
                Thread.sleep(100);      //模拟网络延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print(Thread.currentThread().getName()+"成功购买");
            System.out.println("当前已卖"+used+"张"+"余票数:"+left);
        }
    }

    public static void main(String[] args) {
        BuyTicket bt = new BuyTicket();
        Thread t1 = new Thread(bt,"奥网城");
        Thread t2 = new Thread(bt,"ssssssssssss");
        Thread t3 = new Thread(bt,"Tom");

        t1.start();
        t2.start();
        t3.start();
    }
}

[Result: There will be multiple people buying the same ticket]

Synchronize

public class BuyTicket extends Thread{
    int left = 15;  //总共15张票,余票
    int used = 0;   //已卖票数

    public synchronized boolean buy(){
        if(left<=0)return false;
        left--;
        used++;
        System.out.print(Thread.currentThread().getName()+"成功购买");
        System.out.println(",当前已卖"+used+"张"+",余票数:"+left);
        return true;
    }

    public void run(){
        //取票
        while (true){
            if(!buy())
                break;
            try {
                Thread.sleep(100);  //网络延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

The results show that
Insert picture description here

1. The usage difference between synchronized and lock

synchronized: Add this control to the object that needs to be synchronized. Synchronized can be added to the method or in a specific code block. The parentheses indicate the object that needs to be locked.

lock: Generally use the ReentrantLock class as a lock. The lock and unlock points need to be pointed out through the lock() and unlock() display. Therefore, unlock() is usually written in the finally block to prevent deadlock.

2. The performance difference between synchronized and lock

Synchronized is managed by the JVM for execution,
and lock is the code that controls the lock written in java.

In Java 1.5, synchronize is inefficient. Because this is a heavyweight operation, the operation interface needs to be called, which may lead to the possibility that locking consumes more system time than operations other than locking. In contrast, using the Lock object provided by Java has higher performance.

But with Java 1.6, changes have taken place. Synchronize is very clear in semantics and can be optimized a lot, including adaptive spin, lock elimination, lock coarsening, lightweight locks, biased locks, and so on. As a result, the performance of synchronize on Java 1.6 is not worse than that of Lock. Officials also said that they also support synchronize more, and there is room for optimization in future versions.

The specific difference between the two mechanisms:
Synchronized originally used the CPU pessimistic lock mechanism, that is, the thread obtained the exclusive lock. Exclusive lock means that other threads can only rely on blocking to wait for the thread to release the lock. When the CPU conversion thread is blocked, it will cause thread context switching. When there are many threads competing for locks, it will cause frequent context switching of the CPU, resulting in low efficiency.

And Lock uses an optimistic lock method. The so-called optimistic lock is to complete an operation without locking each time but assuming that there is no conflict. If it fails due to the conflict, it will retry until it succeeds. The mechanism for optimistic locking is the CAS operation (Compare and Swap). We can further study the source code of ReentrantLock, and we will find that one of the more important methods to obtain locks is compareAndSetState. This is actually a special instruction provided by the called CPU.

3. The difference between synchronized and lock usage

There is no difference between synchronized primitive and ReentrantLock in general, but in very complex synchronization applications, please consider using ReentrantLock, especially when you encounter the following two requirements.

1. A thread needs to be interrupted while waiting for the control of a lock.
2. Some wait-notify needs to be processed separately. The Condition application in ReentrantLock can control which thread to notify
. 3. With a fair lock function, each incoming Threads will be queued up

Guess you like

Origin blog.csdn.net/qq_43288259/article/details/113548253