Moment For Technology

Concurrent Programming in Java: Security risks associated with multithreading

Posted on Dec. 3, 2022, 7:55 a.m. by Charles Ellis
Category: The back-end Tag: The back-end java

"This is the 9th day of my participation in the More Text Challenge. For details, see more text Challenge."

Activity problem

A deadlock

Deadlock refers to a deadlock caused by multiple processes competing for resources in the process of running. When a process is in this deadlock state, it will not be able to move forward without external force.

public class ThreadDeadLock {

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";
        new Thread(new HoldThread(lockA, lockB), "threadA").start();
        new Thread(new HoldThread(lockB, lockA), "threadB").start(); }}class HoldThread implements Runnable {

    private final String source1;
    private final String source2;

    public HoldThread(String source1, String source2) {
        this.source1 = source1;
        this.source2 = source2;
    }

    @Override
    public void run(a) {
        synchronized (source1) {
            System.out.println(Thread.currentThread().getName() + "\t holding lock" + source1 + "Try to get" + source2);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (source2) {
                System.out.println(Thread.currentThread().getName() + "\t holding lock" + source2 + "Try to get"+ source1); }}}}// The result: the console will hang because of a deadlock and cannot finish properlyThreadA holds the lock and the lockA tries to acquire the lockB threadB holds the lock and the lockB tries to acquire the lockACopy the code

Locate the deadlock: 1. JPS command to locate the process number. 2

E:\Java\projects\java-concurrent-programingjps 15108 Jps 2932 728 ThreadDeadLock 6364 RemoteMavenServer36 9708 Launcher  E:\Java\projects\java-concurrent-programingjstack 728 2020-06-02 10:08:10 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131 - bl1 mixed mode) : Found one Java - level deadlock: = = = = = = = = = = = = = = = = = = = = = = = = = = = = ="threadB":
  waiting to lock monitor 0x000000001cea0c88 (object 0x000000076b699a98, a java.lang.String),
  which is held by "threadA"
"threadA":
  waiting to lock monitor 0x000000001cea3308 (object 0x000000076b699ad0, a java.lang.String),
  which is held by "threadB"

Java stack information for the threads listed above:
===================================================
"threadB":
        at com.msr.study.concurrent.deadlock.HoldThread.run(ThreadDeadLock.java:42)
        - waiting to lock 0x000000076b699a98 (a java.lang.String)
        - locked 0x000000076b699ad0 (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)
"threadA":
        at com.msr.study.concurrent.deadlock.HoldThread.run(ThreadDeadLock.java:42)
        - waiting to lock 0x000000076b699ad0 (a java.lang.String)
        - locked 0x000000076b699a98 (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.
Copy the code

Jstack and then you get the stack information for your program, which is a lot of stuff. A java-level deadlock was Found.

ThreadB:

- waiting to lock 0x000000076b699a98 (a java.lang.String)

- locked 0x000000076b699ad0 (a java.lang.String)

ThreadA:

-waiting to lock 0x000000076b699ad0 (a java.lang.String)

- locked 0x000000076b699a98 (a java.lang.String)

hunger

Thread starvation can occur if the priority of threads is "uneven" and the CPU is busy so that the low-priority threads have little chance of being executed. The thread holding the lock can also cause "starvation" problems if it takes too long to execute. Hungry, the thread never gets CPU time, it's always hungry.

So when using multithreading, set priorities properly. Use fair locks instead of synchronized because synchronized is not fair locks.

Live lock

A live lock is a process in which a task or performer is not blocked, resulting in repeated trial-failure-trial-fail because some condition is not met. An entity in a live lock is in a state of constant change, and a live lock may open itself.

Live locks are usually caused by incorrect handling of deadlocks. Multiple threads in the deadlock took action at the same time. The way to avoid this is to have only one thread release resources.

Performance issues

  • Elapsed time: The creation and destruction of threads takes time, and when a large number of threads are created and destroyed, the elapsed time is significant, resulting in a loss of performance
  • CPU and memory consumption: If a large number of threads are created, executed and destroyed, this is very CPU and memory consumption, which will directly affect the throughput of the system, resulting in a sharp drop in performance, if the memory resource usage is more than a lot, it is likely to cause OOM
  • Frequent GC execution: The creation and destruction of a large number of threads can easily lead to frequent GC execution, which can cause memory jitter. Memory jitter can cause interface delays on mobile devices
  • Context switching of threads: Data structures shared by the operating system and JVM need to be accessed during thread scheduling. The application, operating system, and JVM all use the same set of cpus, and the more CPU clock cycles are consumed in the JVM and operating system code, the fewer CPU clock cycles are available to the application. When a new thread is switched in, the data it needs may not be in the current processor's local cache, so the context switch will result in some cache missing, and the thread will run slower the first time it is scheduled.

Thread safety issues

Thread-safety issues are probably our biggest concern as developers. Let's take the example of buying tickets now.

package com.msr.study.concurrent.threadsafe;

import java.util.concurrent.TimeUnit;
public class ThreadUnsafe {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        for (int i = 0; i  10; i++) {
            Thread thread = newThread(ticket); thread.start(); }}}class Ticket implements Runnable {
    private static int ticketNum = 50;

    @Override
    public void run(a) {
        while (true) {
            if (ticketNum  0) {
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " sale a ticket,current:" + ticketNum--);
            }else {
                break; }}}}// Output result: where multiple threads appear to sell the same ticket, while remaining 28
Thread-9 sale a ticket,current:28
Thread-8 sale a ticket,current:28
Thread-1 sale a ticket,current:28
Thread-2 sale a ticket,current:29
Thread-4 sale a ticket,current:28
Copy the code

From a bytecode perspective:

Compiled from "ThreadUnsafe.java" class com.msr.study.concurrent.threadsafe.Ticket implements java.lang.Runnable { com.msr.study.concurrent.threadsafe.Ticket(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."init":()V 4: return public void run(); Code: 0: getstatic #2 // Field ticketNum:I 3: ifle 68 6: getstatic #3 // Field java/util/concurrent/TimeUnit.MILLISECONDS:Ljava/util/concurrent/TimeUnit; 9: ldc2_w #4 // long 100l 12: invokevirtual #6 // Method java/util/concurrent/TimeUnit.sleep:(J)V 15: goto 23 18: astore_1 19: aload_1 20: invokevirtual #8 // Method java/lang/InterruptedException.printStackTrace:()V 23: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream; 26: new #10 // class java/lang/StringBuilder 29: dup 30: invokespecial #11 // Method java/lang/StringBuilder."init":()V 33: invokestatic #12 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread; 36: invokevirtual #13 // Method java/lang/Thread.getName:()Ljava/lang/String; 39: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder; 42: ldc #15 // String sale a ticket,current: 44: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder; 47: getstatic #2 // Field ticketNum:I 50: dup 51: iconst_1 52: isub 53: putstatic #2 // Field ticketNum:I 56: invokevirtual #16 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 59: invokevirtual #17 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 62: invokevirtual #18 // Method java/io/PrintStream.println:(Ljava/lang/String;) V 65: goto 0 68: return Exception table: from to target type 6 15 18 Class java/lang/InterruptedException static {}; Code: 0: bipush 50 2: putstatic #2 // Field ticketNum:I 5: return }Copy the code

Isub :ticketNum subtracts by one, putstatic: ticketNum subtracts by one, putstatic: ticketNum subtracts by one. These two operations occur when ticketNum is generated, indicating that ticketNum is not an atomic operation and atoms are not divisible. TickNum can be divided into two operations: minus one, assignment, so this I -- or I ++ these are nonatomic operations.

  47: getstatic     #2                  // Field ticketNum:I
  50: dup
  51: iconst_1
  52: isub
  53: putstatic     #2 
Copy the code

Since time is non-atomic, how can thread safety arise in multiple threads, as shown in the figure below. The JMM is covered a little bit, and volatile will be covered in more detail later. The solution, most simply synchronized. Thread-safety issues are covered in more detail later

conclusion

The use of multithreading will bring a series of problems, if the blind use of multithreading without paying attention to these problems, may bring serious production accidents.

Search
About
mo4tech.com (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.