Thread safety problems and processing methods

Keywords: Java security

Thread safety problems and processing methods

Example: create three windows to sell tickets, with a total number of 100 tickets. Use the way to implement the Runnable interface
1. Problem: in the process of selling tickets, there are duplicate tickets and wrong tickets -- > there are thread safety problems
2. Cause of the problem: when a thread operates the ticket, and the operation has not been completed, other threads participate and operate the ticket.
3. How to solve it: when a thread a is operating a ticket, other threads cannot participate. No other thread can start to operate the ticket until thread a has finished operating the ticket. This situation cannot be changed even if thread a is blocked.
4. In Java, we solve the thread safety problem through synchronization mechanism.

  • Method 1: synchronize code blocks
    Synchronized (synchronized monitor){
    //Code to be synchronized
    }
    explain:
    1. The code that operates shared data is the code that needs to be synchronized. -- > it cannot contain more code or less code.
    2. Shared data: variables operated by multiple threads. For example, ticket is shared data.
    3. Synchronization monitor, commonly known as lock. Any class object can act as a lock.
    Requirement: multiple threads must share the same lock.
    4. Supplement: in the way of implementing Runnable interface to create multithreading, we can consider using this as the synchronization monitor.
  • Mode 2: synchronization method.
    If the code that operates on shared data is completely declared in a method, we might as well synchronize this method declaration.
  • 5. The way of synchronization solves the safety problem of threads. - benefits
  • When operating synchronous code, only one thread can participate and other threads wait. It is equivalent to a single threaded process, which is inefficient. - limitations

1. The synchronous code block handles the thread safety problem of implementing Runnable

class Window1 implements Runnable{

    private int ticket = 100;
//    Object obj = new Object();
//    Dog dog = new Dog();
    @Override
    public void run() {
//        Object obj = new Object();
        while(true){
            synchronized (this){//this: the only object of Window1 / / method 2: synchronized (dog){

                if (ticket > 0) {

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket);


                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}


public class WindowTest1 {
    public static void main(String[] args) {
        Window1 w = new Window1();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("Window 1");
        t2.setName("Window 2");
        t3.setName("Window 3");

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

}


class Dog{

}

2. Use synchronous code block to solve the Thread safety problem of inheriting Thread class

Example: create three windows to sell tickets, with a total of 100 votes. Use the method of inheriting Thread class
Note: in the way of inheriting Thread class to create multithreads, use this carefully as the synchronization monitor, and consider using the current class as the synchronization monitor.

class Window2 extends Thread{


    private static int ticket = 100;

    private static Object obj = new Object();

    @Override
    public void run() {

        while(true){
            //correct
//            synchronized (obj){
            synchronized (Window2.class){//Class clazz = Window2.class,Window2.class will be loaded only once
                //Wrong way: this represents T1, T2 and T3 objects
//              synchronized (this){

                if(ticket > 0){

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(getName() + ": Ticket No.:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }

        }

    }
}


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


        t1.setName("Window 1");
        t2.setName("Window 2");
        t3.setName("Window 3");

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

    }
}

3. Use the synchronization method to solve the thread safety problem of implementing Runnable interface

class Window3 implements Runnable {

    private int ticket = 100;

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

            show();
        }
    }

    private synchronized void show(){//Sync monitor: this
        //synchronized (this){

            if (ticket > 0) {

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket);

                ticket--;
            }
        //}
    }
}


public class WindowTest3 {
    public static void main(String[] args) {
        Window3 w = new Window3();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("Window 1");
        t2.setName("Window 2");
        t3.setName("Window 3");

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

}

4. Use the synchronization method to deal with the Thread safety problem in the way of inheriting the Thread class

class Window4 extends Thread {


    private static int ticket = 100;

    @Override
    public void run() {

        while (true) {

            show();
        }

    }
    private static synchronized void show(){//Synchronization monitor: Window4.class
        //private synchronized void show() {/ / synchronization monitor: t1,t2,t3. This solution is wrong
        if (ticket > 0) {

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + ": Ticket No.:" + ticket);
            ticket--;
        }
    }
}


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


        t1.setName("Window 1");
        t2.setName("Window 2");
        t3.setName("Window 3");

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

    }
}

Summary of synchronization methods:

  1. The synchronization method still involves the synchronization monitor, but we don't need to declare it explicitly.
  2. Non static synchronization method. The synchronization monitor is this
    Static synchronization method. The synchronization monitor is the current class itself

Posted by lazy_yogi on Fri, 22 Oct 2021 19:31:38 -0700