[Big Data] Study Notes 1 Java SE Chapter 9 Multithreading 9.4 Thread Safety

[Big Data] Study Notes

insert image description here

1 Java SE

Chapter 9 Multithreading

9.4 Thread safety

When we use multiple threads to access the same resource (it can be the same variable, the same file, the same record, etc.), if multiple threads only have read operations, then there will be no thread safety issues, but if multiple threads There are read and write operations on resources, which is prone to thread safety issues.

We use a case to demonstrate thread security issues:
a movie theater wants to sell tickets, and we simulate the ticket selling process of the movie theater. Assume that the movie to be played is "Gourd Baby vs. Ultraman", and there are a total of 100 seats for this movie
(only 100 tickets can be sold for this movie).
Let's simulate the ticket window of a movie theater, and realize that multiple windows sell the movie tickets of "Calabash vs. Ultraman" at the same time (multiple windows sell these 100 tickets together)

9.4.1 The same resource problem and thread safety problem

[1] Local variables cannot be shared

package com.dingjiaxiong.unsafe;

/**
 * @Projectname: BigDataStudy
 * @Classname: SaleTicketDemo1
 * @Author: Ding Jiaxiong
 * @Date:2023/4/27 15:59
 */

public class SaleTicketDemo1 {
    
    
    public static void main(String[] args) {
    
    
        Window w1 = new Window();
        Window w2 = new Window();
        Window w3 = new Window();

        w1.start();
        w2.start();
        w3.start();
    }
}

class Window extends Thread {
    
    
    public void run() {
    
    
        int total = 100;
        while (total > 0) {
    
    
            System.out.println(getName() + "卖出一张票,剩余:" + --total);
        }
    }
}

operation result

insert image description here

Result: Found that 300 tickets were sold.

Question: Local variables are independent for each method call, so the total of run() of each thread is independent, not shared data.

[2] Instance variables of different objects are not shared

package com.dingjiaxiong.unsafe;

/**
 * @Projectname: BigDataStudy
 * @Classname: SaleTicketDemo2
 * @Author: Ding Jiaxiong
 * @Date:2023/4/27 16:01
 */

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

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

class TicketSale extends Thread {
    
    
    private int total = 100;

    public void run() {
    
    
        while (total > 0) {
    
    
            System.out.println(getName() + "卖出一张票,剩余:" + --total);
        }
    }
}

insert image description here

Result: Found that 300 tickets were sold.

Problem: Instance variables of different instance objects are independent.

[3] Static variables are shared

Sample code:

package com.dingjiaxiong.unsafe;

/**
 * @Projectname: BigDataStudy
 * @Classname: SaleTicketDemo3
 * @Author: Ding Jiaxiong
 * @Date:2023/4/27 16:02
 */

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

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

class TicketSaleThread extends Thread {
    
    
    private static int total = 100;

    public void run() {
    
    
        while (total > 0) {
    
    
            try {
    
    
                Thread.sleep(10);//加入这个,使得问题暴露的更明显
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(getName() + "卖出一张票,剩余:" + --total);
        }
    }
}

insert image description here

Result: Nearly 100 tickets were found to be sold.

Question (1): But there is a problem of duplicate votes or negative votes.

Reason: thread safety issue

Question (2): If there are two movies to be considered, each sells 100 tickets, etc.

Reason: The static variable of the TicketThread class is shared by all objects of the TicketThread class

[4] Instance variables of the same object are shared

Sample code: Multiple Thread threads use the same Runnable object

package com.dingjiaxiong.unsafe;

/**
 * @Projectname: BigDataStudy
 * @Classname: SaleTicketDemo4
 * @Author: Ding Jiaxiong
 * @Date:2023/4/27 16:03
 */

public class SaleTicketDemo4 {
    
    
    public static void main(String[] args) {
    
    
        TicketSaleRunnable tr = new TicketSaleRunnable();
        Thread t1 = new Thread(tr, "窗口一");
        Thread t2 = new Thread(tr, "窗口二");
        Thread t3 = new Thread(tr, "窗口三");

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

class TicketSaleRunnable implements Runnable {
    
    
    private int total = 100;

    public void run() {
    
    
        while (total > 0) {
    
    
            try {
    
    
                Thread.sleep(10);//加入这个,使得问题暴露的更明显
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余:" + --total);
        }
    }
}

insert image description here

Result: Nearly 100 tickets were found to be sold.

Problem: But there is a problem of duplicate votes or negative votes.

Reason: thread safety issue

[5] Extract resource classes and share the same resource object

Sample code:

package com.dingjiaxiong.unsafe;

/**
 * @Projectname: BigDataStudy
 * @Classname: SaleTicketDemo5
 * @Author: Ding Jiaxiong
 * @Date:2023/4/27 16:04
 */

public class SaleTicketDemo5 {
    
    
    public static void main(String[] args) {
    
    
        //2、创建资源对象
        Ticket ticket = new Ticket();

        //3、启动多个线程操作资源类的对象
        Thread t1 = new Thread("窗口一") {
    
    
            public void run() {
    
    
                while (true) {
    
    
                    ticket.sale();
                }
            }
        };
        Thread t2 = new Thread("窗口二") {
    
    
            public void run() {
    
    
                while (true) {
    
    
                    ticket.sale();
                }
            }
        };
        Thread t3 = new Thread(new Runnable() {
    
    
            public void run() {
    
    
                ticket.sale();
            }
        }, "窗口三");


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

//1、编写资源类
class Ticket {
    
    
    private int total = 100;

    public void sale() {
    
    
        if (total > 0) {
    
    
            try {
    
    
                Thread.sleep(10);//加入这个,使得问题暴露的更明显
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余:" + --total);
        } else {
    
    
            throw new RuntimeException("没有票了");
        }
    }

    public int getTotal() {
    
    
        return total;
    }
}

insert image description here

Result: Nearly 100 tickets were found to be sold.

Problem: But there is a problem of duplicate votes or negative votes.

Reason: thread safety issue

9.4.2 Attempts to address thread safety issues

To solve the above-mentioned security problem of multi-threaded concurrent access to a resource: that is, to solve the problem of duplicate tickets and non-existent tickets, Java provides a synchronization mechanism (
synchronized) to solve.

insert image description here

According to the brief description of the case:

When the window 1 thread enters the operation, the window 2 and window 3 threads can only wait outside. After the operation of window 1 is completed, window 1, window 2 and window 3 have the opportunity to enter the code to execute. That is to say, when a thread modifies a shared resource, other threads cannot modify the resource. Only after the modification is synchronized can they snatch the CPU resource and complete the corresponding operation, ensuring the synchronization of data and solving the problem of thread insecurity. Phenomenon.

In order to ensure that each thread can perform atomic operations normally, Java introduces a thread synchronization mechanism. Note: At any time, at most one thread is allowed to own a synchronization lock, whoever gets the lock will enter the code block, and other threads can only wait outside (BLOCKED).

【1】Principle of Synchronization Mechanism

Synchronization solves the principle of thread safety:

The principle of the synchronization mechanism is actually equivalent to adding a "lock" to a certain piece of code. Any thread that wants to execute this piece of code must first obtain a "lock". We call it a synchronization lock. Because the data of Java objects in the heap is divided into object headers, instance variables, and blank filling. And the object header contains:

  • Mark Word: Records information such as GC and lock marks related to the current object.
  • Pointer to class: Each object needs to record which class it was created from.
  • Array length (only available for array objects)

After which thread acquires the "synchronization lock" object, the "synchronization lock" object will record the ID of this thread, so that other threads can only wait, unless this thread "releases" the lock object, other threads can regain/occupy "Synchronization lock" object.

[2] Synchronization code block and synchronization method

Synchronization method: The synchronized keyword directly modifies the method, indicating that only one thread can enter this method at the same time, and other threads are waiting outside.

public synchronized void method(){
    
    
    可能会产生线程安全问题的代码
}

Synchronized code block: The synchronized keyword can be used in front of a certain block, indicating that only the resources of this block are mutually exclusive.
Format:

synchronized(同步锁){
    
    
     需要同步操作的代码
}

[3] Selection of synchronization lock object

The synchronization lock object can be of any type, but it must be guaranteed that multiple threads competing for the "same shared resource" must use the same "synchronization lock object".

For the synchronization code block, the synchronization lock object is manually specified by the programmer, but for the synchronization method, the synchronization lock object can only be the default,

  • Static method: the Class object of the current class

  • Non-static method: this

[4] Range selection of synchronization code

The scope of the lock is too small: does not solve the security problem

The scope of the lock is too large: because once a thread grabs the lock, other threads can only wait, so the scope is too large, the efficiency will be reduced, and CPU resources cannot be used reasonably.

[5] Code demo

Example 1: Static method locking

package com.dingjiaxiong.safe;

/**
 * @Projectname: BigDataStudy
 * @Classname: SaleTicketDemo3
 * @Author: Ding Jiaxiong
 * @Date:2023/4/27 16:07
 */

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

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

class TicketSaleThread extends Thread {
    
    
    private static int total = 100;

    public void run() {
    
    //直接锁这里,肯定不行,会导致,只有一个窗口卖票
        while (total > 0) {
    
    
            saleOneTicket();
        }
    }

    public synchronized static void saleOneTicket() {
    
    //锁对象是TicketSaleThread类的Class对象,而一个类的Class对象在内存中肯定只有一个
        if (total > 0) {
    
    //不加条件,相当于条件判断没有进入锁管控,线程安全问题就没有解决
            System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余:" + --total);
        }
    }
}

insert image description here

Example 2: Non-static method locking

package com.dingjiaxiong.safe;

/**
 * @Projectname: BigDataStudy
 * @Classname: SaleTicketDemo4
 * @Author: Ding Jiaxiong
 * @Date:2023/4/27 16:07
 */

public class SaleTicketDemo4 {
    
    
    public static void main(String[] args) {
    
    
        TicketSaleRunnable tr = new TicketSaleRunnable();
        Thread t1 = new Thread(tr, "窗口一");
        Thread t2 = new Thread(tr, "窗口二");
        Thread t3 = new Thread(tr, "窗口三");

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

class TicketSaleRunnable implements Runnable {
    
    
    private int total = 1000;

    public void run() {
    
    //直接锁这里,肯定不行,会导致,只有一个窗口卖票
        while (total > 0) {
    
    
            saleOneTicket();
        }
    }

    public synchronized void saleOneTicket() {
    
    //锁对象是this,这里就是TicketSaleRunnable对象,因为上面3个线程使用同一个TicketSaleRunnable对象,所以可以
        if (total > 0) {
    
    //不加条件,相当于条件判断没有进入锁管控,线程安全问题就没有解决
            System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余:" + --total);
        }
    }
}

insert image description here

Example 3: Synchronized code blocks

package com.dingjiaxiong.safe;

/**
 * @Projectname: BigDataStudy
 * @Classname: SaleTicketDemo5
 * @Author: Ding Jiaxiong
 * @Date:2023/4/27 16:08
 */

public class SaleTicketDemo5 {
    
    
    public static void main(String[] args) {
    
    
        //2、创建资源对象
        Ticket ticket = new Ticket();

        //3、启动多个线程操作资源类的对象
        Thread t1 = new Thread("窗口一") {
    
    
            public void run() {
    
    //不能给run()直接假设,因为t1,t2,t3的三个run方法分别属于三个Thread类对象,
                // run方法是非静态方法,那么锁对象默认选this,那么锁对象根本不是同一个
                while (true) {
    
    
                    synchronized (ticket) {
    
    
                        ticket.sale();
                    }
                }
            }
        };
        Thread t2 = new Thread("窗口二") {
    
    
            public void run() {
    
    
                while (true) {
    
    
                    synchronized (ticket) {
    
    
                        ticket.sale();
                    }
                }
            }
        };
        Thread t3 = new Thread(new Runnable() {
    
    
            public void run() {
    
    
                synchronized (ticket) {
    
    
                    ticket.sale();
                }
            }
        }, "窗口三");


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

//1、编写资源类
class Ticket {
    
    
    private int total = 1000;

    public void sale() {
    
    //也可以直接给这个方法加锁,锁对象是this,这里就是Ticket对象
        if (total > 0) {
    
    
            System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余:" + --total);
        } else {
    
    
            throw new RuntimeException("没有票了");
        }
    }

    public int getTotal() {
    
    
        return total;
    }
}

insert image description here

Guess you like

Origin blog.csdn.net/weixin_44226181/article/details/130480144