008 Sync

I. Overview

Multithreading can bring us some improvements in efficiency, but it also brings some other problems. If these problems are not solved, we cannot guarantee that the calculation results of threads are correct.

Well, there is no point in using multithreading at this time.

  Problems brought up:

    [1] Multiple threads share a resource---causing inconsistent resource status

    [2] The execution order of multi-threading, once the thread is running, can we control how the thread runs.

    In this regard, we need to use thread synchronization and thread communication to solve the above problems.


 

2. Resource sharing between threads

When resources are shared among multiple threads, resource usage problems may arise .

  It should be pointed out here that there may be problems, not necessarily problems.

  The first step we need to do now is to understand what kind of situation can go wrong.

    The solution to the problem is to lock ,   

        If you add locks where there is no problem with resource sharing at all, there is no benefit at all other than efficiency.


 

3. When is there a problem with resource sharing?

  Problem 1: When multiple threads are reading a resource problem, but no modification to the resource problem is allowed.

        At this time, there is no need to perform a locking operation at all, because the state of the resource is consistent at all times.

  Problem 2: When multiple threads use a resource problem and can modify it.

      At this time, we need to lock.

        There is only one reason for locking: to ensure the consistent state of resources.

  

The above consistency is harder to understand:

  Consistency means that the state of the resource must be a stable state. When a thread modifies the resource, the state of the resource is not stable during the modification process, so other threads should not see this state.

 


4. Test code

Let's take a very classic example:

  

public  class Ticket {
     // Indicates 100 tickets 
    private  static  int   num = 100 ;
    
    public static void consume() {
        for(;;) {
            if(num > 0) {
                System.out .println (Thread.currentThread().getName()+ " : sell the first " +(num--)+ " ticket " );
            } else 
                return ;
        }
    }
    
    public static void main(String[] args) {
        new Thread("thread1") {
            @Override
            public void run() {
                consume();
            }
        }.start();
        new Thread("thread2") {
            @Override
            public void run() {
                consume();
            }
        }.start();
    }
}

In the above example, we start two threads to sell tickets. Our shared data is the parameter num.

Is there anything wrong with the above example?

Let's revamp it a bit:

public static void consume() {
        for(;;) {
            if(num > 0) {
                try {
                    Thread.sleep(100);
                    System.out .println (Thread.currentThread().getName()+ " : sell the first " +(num--)+ " ticket " );
                } catch (InterruptedException e) {
                    e.printStackTrace ();
                }
            } else 
                return ;
        }
    }

Just in the consume method, put the thread to sleep before printing.

We can see the result:

  

Now there is the concept of 0 tickets, which means that there is a concurrency problem in our example above.


 

5. Problem solving

  Concurrency problems due to resource sharing problems, we can use locks to complete.

  In java, there are many ways to lock. Now we only talk about the simplest ones, and later we will talk about a series of lock mechanisms provided by magic weapons for us.

[1] Method 1: Synchronous code block

  We can do this using the synchronized keyword. 

public static void consume() {
        for(;;) {
            synchronized (Ticket.class) {
                if (num > 0) {
                    try {
                        Thread.sleep(100);
                        System.out .println (Thread.currentThread().getName() + " : sell the first " + ( num-- ) + " ticket " );
                    } catch (InterruptedException e) {
                        e.printStackTrace ();
                    }
                } else 
                    return ;
            }
        }
    }

We add the problematic code to the synchronization code block, then a piece of code only allows the thread that acquired the lock to run, that is to say, the problem of resources being competed by multiple threads at the same time will not occur.

  Note: The lock we use must be said to be the only lock, otherwise there will still be problems.

[2] Method 2. Use synchronous method 

public synchronized static void consume() {
        for(;;) {
                if (num > 0) {
                    try {
                        Thread.sleep(100);
                        System.out .println (Thread.currentThread().getName() + " : sell the first " + ( num-- ) + " ticket " );
                    } catch (InterruptedException e) {
                        e.printStackTrace ();
                    }
                } else 
                    return ;
            }
    }

Here we use the synchronization method, which is actually the same as the synchronization method. It also locks the entire method. This method only allows one thread to access. 

 


6. Synchronized methods and synchronized code blocks

  Essentially it's the same thing,

  When we execute the above synchronization method, we will find that only one thread is working in our concurrency.

  Why? The reason is that once a thread seizes the lock, it sells all the tickets.

Now let's do it another way:  

public  class Ticket {
     // Indicates 100 tickets 
    private  static  int num = 100 ;

    public synchronized static void consume() {
        if (num > 0) {
            try {
                Thread.sleep(100);
                System.out .println (Thread.currentThread().getName() + " : sell the first " + ( num-- ) + " ticket " );
            } catch (InterruptedException e) {
                e.printStackTrace ();
            }
        }
    }

    public static void main(String[] args) {
        new Thread("thread1") {
            @Override
            public void run() {
                for(;;)
                consume();
            }
        }.start();
        new Thread("thread2") {
            @Override
            public void run() {
                for(;;)
                consume();
            }
        }.start();
    }
}

Our current thread logic unit is just selling a ticket.

Then we can see the alternate running of threads again.

Summarize :

  [1] The lock of the static synchronization method is the class

  [2] The object locked by ordinary methods is this.

 

  

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325027826&siteId=291194637