Java's Three Souls and Seven Spirits - Advanced Multithreaded

Catalog

1. Multithreaded Creation

==There are four ways to create multithreads!!!#F44336==

1. Create a Thread subclass

Code first:

/**
 * One way to create multithreading is:
 *      Create a subclass that inherits Thread
 * 
 * @author 🏹☂࿈Autumn Parrot
 * @create 2020/3/7 18:41
 */

//Thread Class
class NumCount extends Thread{

    //run method is the code to execute
    @Override
    public void run() {
        //Output 0-99
        for (int i = 0; i < 100; i++) {
           System.out.println(NumCount.currentThread().getName()+":"+i);
        }
    }
}
//Main Class
public class MyThread {
    public static void main(String[] args) {
        //Objects that create Thread subclasses
        NumCount nc1 = new NumCount();
        //Name the thread
        nc1.setName("Count Thread 1");
        //Open Thread
        nc1.start();
    }
}

Matters needing attention

  • run() method is the execution content of the thread
  • The created object needs to call the start() method to open the thread (inheritance)
  • NumberCount.currentThread().getName() is to get the name of the current thread (inheritance)

2. Implement Runnable Interface

Code first:

/**
 * Method two for creating multithreads:
 *      Implement Runnable Interface
 *
 * @author 🏹☂࿈Autumn Parrot
 * @create 2020/3/7 18:41
 */

//Thread Class
class NumCount implements Runnable{

    //run method is the code to execute
    @Override
    public void run() {
        //Output 0-99
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
//Main Class
public class MyThread {
    public static void main(String[] args) {
        //Instantiate Runnable Interface
        NumCount numberCount = new NumCount();
        //Instantiate a thread with the constructor parameter being the instantiated interface
        Thread nc1 = new Thread(numberCount);
        //Name the thread
        nc1.setName("Count Thread 1");
        //Open Thread
        nc1.start();
    }
}

Matters needing attention

  • Because it implements the Runnable interface, the NunberCount class cannot be used to get the thread name
  • To create a thread, you need to instantiate the interface before creating a thread object

3. Implement Callable Interface

Code first:

/**
 * Method three for creating multithreads:
 *      Implement Callable Interface - - JDK5.0 New
 *
 * @author 🏹☂࿈Autumn Parrot
 * @create 2020/3/7 18:41
 */

//Thread Class
class NumCount implements Callable<Integer> {

    //Override call() method
    @Override
    public Integer call() throws Exception {
        //Calculate the sum of 1-100 numbers
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
            sum += i;
        }
        return sum;
    }
}
//Main Class
public class MyThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //Create object of Callable interface implementation class
        NumCount numCount = new NumCount();
        //Pass objects of Callable interface implementation class to FutureTask constructor to create FutureTask objects
        FutureTask<Integer> futureTask = new FutureTask<>(numCount);
        //Create thread, pass FutureTask object to Thread constructor
        Thread nc1 = new Thread(futureTask);
        nc1.setName("Count Thread 1");
        nc1.start();

        //Gets the return value of the call method in Callable
        Integer sum = futureTask.get();
        System.out.println("The sum is:" + sum);
    }
}

Matters needing attention

  • Callable method updated in jdk 1.5
  • FutureTask implements the Runnab interface, so you can pass in an instantiated object of FutureTask when you create a thread
  • Callable is more flexible than Runnable, supports generics, and can return values
  • The return value of the call() method is obtained by the get() method of FutureTask

4. Create a thread pool

Code first:

/**
 * Method four for creating multithreads:
 *      Create Thread Pool
 *
 * @author 🏹☂࿈Autumn Parrot
 * @create 2020/3/7 18:41
 */

//Thread Class
class NumCount implements Runnable {

    //Override run method
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
//Main Class
public class MyThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //Create a thread pool with a specified number of threads
        ExecutorService service = Executors.newFixedThreadPool(10);
        //To perform an operation on a specified thread, you need to provide an object that implements the Runnable or Callable interface implementation class
        service.execute(new NumCount());//For Runnable
//        service.submit();//for Callable
        //Close Thread Pool
        service.shutdown();
    }
}

Matters needing attention

  • Thread pool supports Runnable and allable objects
  • See another thread pool for more details

2. Thread Security Issues

Problem introduction:

  • Train ticket problem: three ticket windows sell 100 tickets at the same time

Problem Analysis:

  1. Multi-threading is required for three windows to buy tickets at the same time
  2. There is a shared data during the ticket buying process: the number of tickets
  3. Thread security issues occur during execution: repeatedly selling the same ticket and selling the wrong ticket

Q: Why is there a wrong ticket for a duplicate ticket?
A: When the limit occurs: when multiple threads call the shared data at the same time and the thread has not finished, there will be a situation where the same value is called repeatedly (duplicate votes), and when the number of votes does not meet the number of threads, there will be a negative number (wrong votes).

##### Solution: Thread Synchronization Mechanism
Thread synchronization mechanism:
When one thread is using shared data (executing synchronized code), other threads wait regardless of whether the thread is blocked or not.

    If you don't understand it, imagine the female friends queuing at the entrance of the women's toilet in a tourist attraction.

How to do this:

1. Synchronize Code Blocks
This approach involves synchronized modifiers.
Basic format:

Synchronized (synchronization monitor (lock){
 * //Code to be synchronized
 *      }

Explain:

  1. Code that manipulates shared data is the code to be synchronized
  2. Shared data: the same data that multiple threads work together
  3. Synchronization monitors, commonly known as locks, can act as objects of any class.
  4. Synchronized threads must share a lock

Example use:

/**
 * Resolving thread security issues with synchronous code blocks
 *
 * Question: Three windows sell 100 tickets and solve with thread.
 *
 *
 * @author 🏹☂࿈Autumn Parrot
 * @create 2020/3/5 17:50
 */

class TicketTread extends Thread{

    //Ensuring that multiple threads share a single piece of data requires setting it to static
    public static int ticket = 100;
    //Same Lock
    public static Object object = new Object();

    @Override
    public void run() {
        while (true){
            //Synchronize Code Blocks
            synchronized (object){
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(TicketTread.currentThread().getName() + ":Selling No." + ticket + "Tickets");
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}


public class TicketDemo {
    public static void main(String[] args) {
        TicketTread t1 = new TicketTread();
        TicketTread t2 = new TicketTread();
        TicketTread t3 = new TicketTread();

        t1.setName("Window One");
        t2.setName("Window 2");
        t3.setName("Window Three");

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

2. Synchronization method
This approach involves synchronized modifiers.
Basic format:

synchronized data type method name (){
    //Code that needs to be synchronized
}

Be careful:

  • There is still a synchronization monitor (lock) by default, this

Example use:

class Windows1 implements Runnable{

    //There is no need to set static variables here because multiple threads call the same interface
    public int ticket = 100;
    Object object = new Object();

    @Override
    public void run() {
        while (true){
               show();
        }

    }
    //Synchronization method
    public synchronized void show(){
        if (ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(TicketTread.currentThread().getName() + ":Selling No." + ticket + "Tickets");
            ticket--;
        }
    }
}


public class TicketDemo2 {
    public static void main(String[] args) {
        Windows1 windows = new Windows1();
        Thread t1 = new Thread(windows);
        Thread t2 = new Thread(windows);
        Thread t3 = new Thread(windows);

        t1.setName("Window One");
        t2.setName("Window 2");
        t3.setName("Window Three");

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

3. ReentrantLock
New features for jdk1.5
Usage is similar to synchronizing code blocks.Replace the code block with a method call.
Example use:

class Windows implements Runnable{

    public static int ticket = 100;
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {

                //Call Locked Method
                lock.lock();

                //Code after lock is equivalent to synchronizing blocks of code

                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":Selling No." + ticket + "Tickets");
                    ticket--;
                } else {
                    break;
                }
            } finally {
                //Call unlock method
                lock.unlock();
            }

        }
    }
}

public class LockTest {
    public static void main(String[] args) {
        Windows windows = new Windows();
        Thread t1 = new Thread(windows);
        Thread t2 = new Thread(windows);
        Thread t3 = new Thread(windows);

        t1.setName("Window One");
        t2.setName("Window 2");
        t3.setName("Window Three");

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

Interview Question: What is the difference between synchronized and Lock?

  • Same thing: they are all thread synchronization mechanisms
  • Difference:
    • The synchornized mechanism automatically releases the synchronization monitor after executing the corresponding synchronization code.
    • Lock requires manual start of synchronization (lock()) and end of synchronization (unlock())

3. Thread Communication Problems

Problem introduction:

Two threads printing 1-100, alternating
Problem code:

/**
 * Two threads printing 1-100, alternating
 *
 * @author 🏹☂࿈Autumn Parrot
 * @create 2020/3/6 22:41
 */

class Number implements Runnable{

    private int number = 1;

    @Override
    public void run() {
        while (true){
            synchronized (this) {
                if (number<=100){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":Numbers:" + number);
                    number++;
                }else {
                    break;
                }
            }
        }
    }
}

public class Communicate {
    public static void main(String[] args) {
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);

        t1.setName("Thread 1");
        t2.setName("Thread 2");

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

When you run this code, a single thread runs

Problem Analysis:

t1 threads consistently hold locks and cannot print alternately

Solution: Thread communication

Three swordsmen need to be used: wait(), notify(), notifyAll()

  • wait(): Once this method is executed, the current thread enters a blocking state and releases the synchronization monitor (lock)
  • notify(): Once this method is executed, one of the waited threads will be waked up, and if more than one thread is waiting, the one with the highest priority will be waked up.
  • notifyAll(): Once this method is executed, all wait ed threads are awakened.

Explain:

  1. The three methods must be used in the synchronization block or synchronization method
  2. The caller of the three methods must be the synchronization monitor in the synchronization block or method (lock is the caller, only lock can call three methods) otherwise IllegalMonitorStateException exception will occur
  3. Three methods are defined in the java.lang.Object class

Specific methods:

/**
 * Two threads printing 1-100, alternating
 *
 * @author 🏹☂࿈Autumn Parrot
 * @create 2020/3/6 22:41
 */

class Number implements Runnable{

    private int number = 1;

    @Override
    public void run() {
        while (true){
            synchronized (this) {
                //Wake up a thread using the notify() method
//              this.notify();
                notify();
                if (number<=100){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":Numbers:" + number);
                    number++;

                    try {
                        //Using the wait() method to keep the thread blocked
//                      this.wait();
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }else {
                    break;
                }
            }
        }
    }
}

public class Communicate {
    public static void main(String[] args) {
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);

        t1.setName("Thread 1");
        t2.setName("Thread 2");

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

Interview Question: What are the similarities and differences between sleep() and wait()?

  • Same point: once a method is executed, the current thread can be blocked
  • Difference:
    • The location of the two method declarations is different: sleep () in the Thread class and wait() in the Object class.
    • Calls require different requirements: sleep() can be called in any scenario you want.wait() must be used in synchronization blocks and methods
    • About whether to release the synchronization monitor: If both methods are used in the synchronization block or synchronization method, sleep() will not release the lock, wait() will release the lock

4. More examples

1. Solve thread security issues in singleton mode by using thread synchronization

Problem solving:

/**
 * Solving thread security issues in singleton mode using thread synchronization
 *
 * @author 🏹☂࿈Autumn Parrot
 * @create 2020/3/6 16:32
 */
public class BankDemo {
}

class Bank{
    //Singleton pattern (private construction method to ensure that this class uses objects uniquely)
    private Bank(){};

    public static Bank instence = null;

    //Thread security issues may arise here
    public static Bank getInstence(){

        //Mode 1, slightly less efficient (threads are all synchronized)
//        synchronized (Bank.class) {
//            if (instence==null){
//                instence = new Bank();
//            }
//        return instence;
//        }
        //Mode 2: Higher efficiency (a small number of threads synchronized)
        if (instence==null){
            synchronized (Bank.class) {
                if (instence==null){
                    instence = new Bank();
                }
            }
        }
        return instence;
    }
}

2. Bank deposits (thread security)

Description of the problem:
The bank has an account where two depositors deposit 3,000 yuan into the same account, 1,000 yuan at a time, 3 times, and each time print balance is saved.
Problem solving:

/**
 * @author 🏹☂࿈Autumn Parrot
 * @create 2020/3/6 22:19
 */

//Accounts (Shared Data)
class Account{

    private double balance;
    public Account(double balance){
        this.balance =balance;
    }

    //Save money
    public synchronized void deposit(double amt){

        if (amt > 0){
            balance += amt;
            System.out.println(Thread.currentThread().getName() + ": Save money successfully!The current balance is:" + balance);
        }
    }
}

//Store (Thread)
class Customer implements Runnable{

    private Account acct;
    //Initialize data
    public Customer(Account acct){
        this.acct = acct;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            acct.deposit(1000);
        }
    }
}

public class AccountTest {
    public static void main(String[] args) {
        Account acct = new Account(0);
        Customer customer = new Customer(acct);
        Thread c1 = new Thread(customer);
        Thread c2 = new Thread(customer);

        c1.setName("nail");
        c2.setName("B");

        c1.start();
        c2.start();
    }
}

3. Producer-consumer issues (threaded communication issues)

Description of the problem:
The producer gives the product to the Clerk, and the consumer takes it from the Clerk.A shop assistant can only hold a fixed number of products at a time (e.g. 20). If a manufacturer tries to produce more products, the shop assistant will ask the manufacturer to stop. If there is space in the store to place the product, the shop assistant will tell the consumer to wait. If there is no product in the store, the shop assistant will tell the consumer to take the product again.
Problem Analysis:

1. Is it a multi-threaded problem?Yes, producer thread, consumer thread
2. Is there shared data?Yes, shop assistant (or product)
3. How to solve the existing security problems?Synchronization mechanism, there are three ways
4. Do you want to design communication between threads?yes

Problem solving:

/**
 * @author 🏹☂࿈Autumn Parrot
 * @create 2020/3/7 11:04
 */
class Clerk{

    private int productCount = 0;

    //yield a product
    public synchronized void produceProduct() {
        if (productCount<20){
            productCount++;
            System.out.println(Thread.currentThread().getName() + ": Production in progress" + productCount + "Products");
            notify();
        }else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //Consumer Products
    public synchronized void consumeProduct(){
        if (productCount>0){
            System.out.println(Thread.currentThread().getName() + ": Consuming" + productCount + "Products");
            productCount--;
            notify();
        }else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Producer implements Runnable{//Producer

   private Clerk clerk;

    public Producer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":Start production....");

        while (true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
}

class Consumer implements Runnable{//Consumer

    private Clerk clerk;

    public Consumer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":Start Consumer Products....");

        while (true){
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}

public class Product {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Producer producer = new Producer(clerk);
        Thread p1 = new Thread(producer);
        p1.setName("Producer 1");

        Consumer consumer = new Consumer(clerk);
        Thread c1 = new Thread(consumer);
        c1.setName("Consumer 1");
        Thread c2 = new Thread(consumer);
        c2.setName("Consumer 2");

        p1.start();
        c1.start();
        c2.start();
    }
}

Tags: Java Windows JDK less

Posted on Sat, 07 Mar 2020 11:39:35 -0500 by wemustdesign