Java multithreading (Continued)

Posted by listenmirndt on Mon, 01 Nov 2021 09:18:25 +0100

1, Methods related to thread scheduling  

1.void setPriority(int newPriority) sets the priority of the thread

2.int getPriority() get thread priority

package com.java.javase.Thread;
public class ThreadTest08 {
    public static void main(String[] args) {
        Thread currentThread=Thread.currentThread();
        System.out.println(currentThread.getName()+"The default priority of threads is:"+currentThread.getPriority());
        Thread t=new Thread(new MyThread8());
        t.setName("t");
        t.start();
    }
}
class MyThread8 implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"The default priority of threads is:"+Thread.currentThread().getPriority());
    }
}

Output results:

The default priority of the main thread is: 5
The default priority of t thread is: 5

3.Thread.yield() pause the current thread  

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) {
        /**
         * Make way for the current thread to pause, return to the ready state, and give way to other threads
         * Static method: Thread.yield();
         */
        Thread t=new Thread(new MyRunable());
        t.setName("t");
        t.start();
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}
class MyRunable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            // Give way every 10
            if(i%10==0){
                Thread.yield(); // Pause the current thread and give it to the main thread
            }
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}

4.void join() merge thread   

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main begin");
        Thread t=new Thread(new MyRunable());
        t.setName("t");
        t.start();
        // Merge thread
        t.join(); // t is merged into the current thread, the current thread is blocked, the t thread executes until the end, and the stacks are coordinated
        System.out.println("main over");
    }
}
class MyRunable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}

  Output results:

main begin
t--->0
t--->1
t--->2
t--->3
t--->4
main over

2, Thread synchronization mechanism  

Without thread synchronization mechanism - simulate two threads withdrawing money from the same account

Account class  

package com.java.javase.ThreadSafe;
/**
bank account
 */
public class Account {
    private String no;
    private double balance;
    public Account() {
    }
    public Account(String no, double balance) {
        this.no = no;
        this.balance = balance;
    }
    public String getNo() {
        return no;
    }
    public void setNo(String no) {
        this.no = no;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
    // Provide a method of withdrawal
    public void withDraw(double money){
        // Thread synchronization mechanism is not used
        // t1 and t2 use this method concurrently (t1 and t2 are two stacks, and the two stacks operate on the same object in the heap)
         double before=this.getBalance();
         double after=before-money;
         this.setBalance(after);
    }
}

Thread class

package com.java.javase.ThreadSafe;

public class AccountThread extends Thread {
    // Two threads must share the same account object
    private Account act;
    public AccountThread(Account act){
        this.act=act;
    }
    @Override
    public void run() {
        double money=5000;
        act.withDraw(money);
        System.out.println(Thread.currentThread().getName()+"Successful withdrawal of 001 account "+"Remaining balance:"+act.getBalance()  );
    }
}

Test class

package com.java.javase.ThreadSafe;

public class Test {
    public static void main(String[] args) {
        // It is equivalent to the same bank card. The attributes include the account number and balance of the card
        Account act=new Account("001",10000);
        // The same object in two stack operation heaps is equivalent to two people operating on the same bank card
        Thread t1=new AccountThread(act);
        Thread t2=new AccountThread(act);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

In case of safety:

t2 successful withdrawal of 001 account, remaining balance: 0.0
t1 successfully withdrew from 001 account, remaining balance: 5000.0

However, thread safety problems occur:

t2 successful withdrawal of 001 account, remaining balance: 5000.0
t1 successfully withdrew from 001 account, remaining balance: 5000.0

How to use thread synchronization mechanism to solve thread safety problems:

We only need to change the withdrawal method in the account class to:

package com.java.javase.ThreadSafe;
/**
bank account
 */
public class Account {
    private String no;
    private double balance;
    public Account() {
    }
    public Account(String no, double balance) {
        this.no = no;
        this.balance = balance;
    }
    public String getNo() {
        return no;
    }
    public void setNo(String no) {
        this.no = no;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
    // Provide a method of withdrawal
    public void withDraw(double money) {
        // Using thread synchronization
          synchronized (this){
            // These lines of code must be queued and cannot be concurrent
            // Syntax of thread synchronization mechanism:
            // Synchronized() {thread synchronized code block}
            // The content in () is critical
              /**
               * ()What does it say?
               * That depends on which threads you want to synchronize?
               * Suppose you only want t1, t2 and t3 threads to queue for execution there, you need to write an object shared by t1, t2 and t3 in ()
               * The shared objects here are: account objects
               */
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
            }
            double before=this.balance;
            double after=before-money;
            this.setBalance(after);
          }
    }
}

synchronized execution principle:

1. Assuming that t1 and t2 threads are concurrent, when starting to execute synchronous code blocks, there must be one first and one later

2. Suppose t1 executes first and encounters the synchronized keyword. At this time, it will automatically find the object lock of "shared object behind". After finding it, it will occupy the lock, and then execute the program in the synchronization code block. It will always occupy the lock during program execution, and the lock will not be released until the execution of the synchronization code block is completed

3. Suppose t1 has occupied the lock, and t2 also encounters the synchronized keyword, and will also occupy the lock of the shared object. As a result, the lock has been occupied by t1. t2 can only wait for the end of t1 outside the synchronization code block until t1 completes the execution of the synchronization code block and t1 returns the lock. At this time, t2 finally waits for the lock, and then t2 occupies the lock to enter synchronization The code block executes the program, so that the thread is queued for execution

synchronized can appear on the instance method:

package com.java.javase.ThreadSafe;
/**
bank account
 */
public class Account {
    private String no;
    private double balance;
    public Account() {
    }
    public Account(String no, double balance) {
        this.no = no;
        this.balance = balance;
    }
    public String getNo() {
        return no;
    }
    public void setNo(String no) {
        this.no = no;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
    // Provide a method of withdrawal

    /**
     * Can I use synchronized on instance methods
     * synchronized In the instance method, this must be locked. This method is not flexible
     * There is another disadvantage: synchronized appears on the instance method, indicating that the whole method body needs to be synchronized
     *              The scope of synchronization may be expanded for no reason, resulting in the reduction of program execution efficiency, so this method is not commonly used
     *            Advantages: the code is concise. If the shared object must be this, this method is recommended
     */
    public synchronized void withDraw(double money) {
        double before=this.balance;
        double after=before-money;
        try{
            Thread.sleep(1000);
        }catch (InterruptedException e){
        }
        this.setBalance(after);
    }
}

3, Exercises   

Exercise 1:  

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
       MyClass mc=new MyClass();
       Thread t1=new MyThread(mc);
       Thread t2=new MyThread(mc);
       t1.setName("t1");
       t2.setName("t2");
       t1.start();
       Thread.sleep(1000); // Ensure that the t1 thread executes first
       t2.start();
    }
}
class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc){
        this.mc=mc;
    }
    public void run(){
        if(Thread.currentThread().getName().equals("t1")){
            mc.doSome();
        }else if(Thread.currentThread().getName().equals("t2")){
            mc.doOther();
        }
    }
}
class MyClass{
    public synchronized void doSome(){
        System.out.println("doSome begin");
        try{
            Thread.sleep(1000*10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    /**
     * doOther Does the execution of the method need to wait for the end of the doSome method?
     *  No, because the execution of the method does not need to acquire a lock
     */
    public void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

Output results:  

doSome begin
doOther begin
doOther over
doSome over

What happens if you add synchronized to the do other method in the above question?

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
       MyClass mc=new MyClass();
       Thread t1=new MyThread(mc);
       Thread t2=new MyThread(mc);
       t1.setName("t1");
       t2.setName("t2");
       t1.start();
       Thread.sleep(1000); // Ensure that the t1 thread executes first
       t2.start();
    }
}
class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc){
        this.mc=mc;
    }
    public void run(){
        if(Thread.currentThread().getName().equals("t1")){
            mc.doSome();
        }else if(Thread.currentThread().getName().equals("t2")){
            mc.doOther();
        }
    }
}
class MyClass{
    public synchronized void doSome(){
        System.out.println("doSome begin");
        try{
            Thread.sleep(1000*10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    /**
     * doOther Does the execution of the method need to wait for the end of the doSome method?
     *  synchronized Appears on the instance method, indicating that the lock is this
     *  Yes, because the execution of the method needs to obtain a lock
     */
    public synchronized void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

Output results:

doSome begin
doSome over
doOther begin
doOther over

synchronized appears on static methods:

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
       MyClass mc=new MyClass();
       Thread t1=new MyThread(mc);
       Thread t2=new MyThread(mc);
       t1.setName("t1");
       t2.setName("t2");
       t1.start();
       Thread.sleep(1000); // Ensure that the t1 thread executes first
       t2.start();
    }
}
class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc){
        this.mc=mc;
    }
    public void run(){
        if(Thread.currentThread().getName().equals("t1")){
            mc.doSome();
        }else if(Thread.currentThread().getName().equals("t2")){
            mc.doOther();
        }
    }
}
class MyClass{
    public synchronized static void doSome(){
        System.out.println("doSome begin");
        try{
            Thread.sleep(1000*10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }

    /**
     * doOther Does the execution of the method need to wait for the end of the doSome method?
     *  synchronized Appears on a static method, indicating that the lock is a class lock
     *  There can be multiple objects, but there is only one class lock, so you need to wait
     */
    public synchronized static void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

doSome begin
doSome over
doOther begin
doOther over

4, Deadlock 🔒

package com.java.javase.deadLock;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        Object o2=new Object();
        // t1 and t2 share o1 and o2
        Thread t1=new MyThread1(o1,o2);
        Thread t2=new MyThread2(o1,o2);
        t1.start();
        t2.start();
    }
}
class MyThread1 extends Thread{
   Object o1;
   Object o2;
   public MyThread1(Object o1,Object o2){
      this.o1=o1;
      this.o2=o2;
   }
    @Override
    public void run() {
        synchronized (o1){
            try {
                Thread.sleep(1000*5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o2){

            }
        }
    }
}
class MyThread2 extends Thread{
    Object o1;
    Object o2;
    public MyThread2(Object o1,Object o2){
        this.o1=o1;
        this.o2=o2;
    }
    @Override
    public void run() {
        synchronized (o2){
            try {
                Thread.sleep(1000*5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o1){

            }
        }
    }
}

V   Daemon threads and timers

package com.java.javase.Thread;
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
       Thread t1=new MyThread();
       t1.setName("Thread backing up data");
       // Set the thread as a daemon before starting the thread
        t1.setDaemon(true);
        t1.start();
       // Main thread: user thread
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"---"+i);
            Thread.sleep(1000);
        }
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        // Even if it is an endless loop, because the thread is a daemon thread, when the user thread ends, the daemon thread automatically stops
       int i=0;
       while (true) {
           System.out.println(Thread.currentThread().getName() + "---" + (i++));
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
    }
}

Thread backing up data -- 7
Thread backing up data -- 8
main---8
main---9
Thread backing up data -- 9
Thread for backing up data -- 10

Process finished with exit code 0

Timer:  

package com.java.javase.Timer;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTest01 {
    public static void main(String[] args) throws Exception{
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date firstTime=sdf.parse("2021-11-01 14:36:00");
        // Create timer object
        Timer timer=new Timer();
        // Three parameters: specify the scheduled task, the first execution time, and how often to execute it
        timer.schedule(new LogTimerTask(),firstTime,1000*10);

    }
}
// Write a scheduled task class
// TimerTask implements the Runnable interface
class LogTimerTask extends TimerTask{
    @Override
    public void run() {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        String strTime=sdf.format(new Date());
        System.out.println(strTime+":Completed a data backup");
    }
}

2021-11-01 14:36:00 016: a data backup was completed
2021-11-01 14:36:10 019: a data backup was completed
2021-11-01 14:36:20 035: a data backup was completed

6, The third way to implement threads is to implement the Callable interface

package com.java.javase.Thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 *  The third way to implement threads:
 *      Implement Callable interface
 */
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // Create a future task class object
        // You need to implement a class object for a Callable interface
        FutureTask futureTask=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                // The call() method is equivalent to the run method, but it has a return value
                // A thread executes a task, and there may be an execution result after execution
                System.out.println("call begin");
                Thread.sleep(1000*10);
                System.out.println("call over");
                return new Object();
            }
        });
        // Create thread object
        // Here is the main method, in the main thread
        // How to get the return result of t thread in the main thread?
        // The execution of the get() method will cause the current thread to block
        Thread t=new Thread(futureTask);
        t.start();
        futureTask.get();
        // The program here of the main method must wait for the end of the get () method to execute
        // The get () method can take a long time
        System.out.println("Get the execution results");
    }
}

7, wait and notify methods in Object class  — Producer and consumer model

package com.java.javase.ProducerAndCustomer;

import java.util.ArrayList;
import java.util.List;
// Using the wait method and notify method to implement the "producer and consumer model"
public class ThreadTest {
    public static void main(String[] args) {
        List list=new ArrayList();
        Thread t1=new Thread(new Producer(list));
        Thread t2=new Thread(new Customer(list));
        t1.setName("Producer thread");
        t2.setName("Consumer thread");
        t1.start();
        t2.start();
    }
}
// Production thread
class Producer implements Runnable{
    // Warehouse
    private List list;
    public Producer(List list) {
        this.list = list;
    }
    @Override
    public void run() {
        while(true){
            // Lock the warehouse object list
            synchronized (list){
                // Greater than 0 indicates that there is already an element in the warehouse
                if(list.size()>0){
                    try {
                        // The current thread enters the waiting state and releases the lock of the list collection previously occupied by the Producer
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Object obj = new Object();
                list.add(obj);
                System.out.println(Thread.currentThread().getName() + "-----Produced a" + obj);
                list.notifyAll();
            }
        }
    }
}
// Consumption thread
class Customer implements Runnable{
    private List list;
    public Customer(List list) {
        this.list = list;
    }
    @Override
    public void run() {
        while(true){
            synchronized (list){
                if(list.size()==0){
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Object obj=list.remove(0);
                System.out.println(Thread.currentThread().getName()+"-----Consumed one"+obj);
                list.notifyAll();
            }
        }
    }
}


Topics: Java Multithreading