The use of synchronized in Java multithreading (v)

Source: Internet
Author: User
Tags thread class

Concurrent programming brings us a lot of convenience, but it also poses a thread-safety problem.

Thread Safety

Definition of thread safety:

When multiple threads access a class, the class can always represent the correct behavior, then it is called thread-safe.

The reasons for this can be summed up as follows:

1. Sharing data: Only shared data can create security issues. If it is a variable declared inside a method, it is in the virtual machine stack and is exclusive to each thread, and there is no security issue.

2. Multiple threads perform simultaneous operations on shared data. Multithreading makes simultaneous operations on the same shared data, at which point the shared data affects each other.

This is an example of a thread-safe problem:

public class UnsafeThread implements Runnable {    private static int count = 0;    public void increase(){        count++;    }    public void run() {        for (int i = 0; i < 2000000; i++) {            increase();        }    }    public static void main(String[] args) {        UnsafeThread myThread = new UnsafeThread();        Thread thread1 = new Thread(myThread);        Thread thread2 = new Thread(myThread);        thread1.start();        thread2.start();        try {            thread1.join();            thread2.join();            System.out.println(count);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

After execution, two threads, each thread has a secondary self-increment for count, and the 2000000 expected result should be 4000000 , however, the results are basically not the right one, each time not the same, but 4000000 smaller. Why?

The steps of i++ should be:

Read-to-change-write

However, if a thread is not written back while it is being read, and the other thread is reading, then one operation is equivalent to being ineffective, resulting in fewer results than expected.

Mutual exclusion Lock

Therefore, in order to solve these problems, we think of the countermeasures:

    1. Eliminate shared data: The idea is good, but in some cases it is impossible to completely eliminate, and we can only reduce the sharing of data as much as possible.
    2. At the same time, only one thread can operate on the shared data, and the other thread waits until the thread has finished processing and then takes action.

Mutexes can solve this kind of problem.

Features of mutual exclusion locks

  1. The thread acquires the lock automatically before it enters the synchronization code block, and the lock is automatically released when the synchronization code block exits.
  2. At the same time, only one thread can hold this lock. When A a thread attempts to acquire a lock held by a thread, the thread B A must wait or block until the thread B releases the lock. If you B do not release this lock, you will A need to wait all the time.
  3. Reentrant: When the outer function of the same thread acquires the lock, the inner recursive function still has the code to acquire the lock, but it is not affected.
Built-in lock synchronized

In this article, we discuss a built-in mutex that is provided by Java, which is used synchronized to decorate:

 synchronized(lock){    // 访问或修改共享数据 }

For just the problem, we just need to add a keyword to

public synchronized void increase(){    count++;}

The result of the final output is necessarily 4000000 .

A synchronized decorated block of code is executed atomically (a set of statements that form an indivisible unit). Any thread that executes a synchronous code block does not see a synchronous block of code that another thread is executing that is protected by the same lock.

synchronizedThere are three ways to use it:

    1. Normal synchronization method, the lock is the current instance object (this);
    2. Static synchronization method, the lock is the class object of the current classes;
    3. Synchronous code block, the lock is the object inside the parentheses.
Normal synchronization method, lock is the current instance object (this)

Common synchronization method, lock is the current this object, in the above code, we verify the effect of mutual exclusion.

Verifies that the lock object in the normal method is the same.

Used if two functions increase and decrease normal functions are in the same object synchronized . When a function is running, the lock has been obtained by one thread, and if another line threads to enter another function, it will not go in.

public class MyThread implements Runnable {private int state=0;    private static int count = 0;        Public synchronized void Increase () {System.out.println (System.currenttimemillis () + "increase begin");        try {thread.sleep (5000L);        } catch (Interruptedexception e) {e.printstacktrace ();    } System.out.println (System.currenttimemillis () + "Increase end");        } public synchronized void decrease () {System.out.println (System.currenttimemillis () + "decrease begin");        try {thread.sleep (10000L);        } catch (Interruptedexception e) {e.printstacktrace ();    } System.out.println (System.currenttimemillis () + "decrease End");            public void Run () {if (state = = 0) {state = 1;        Increase ();            }else{state = 0;        Decrease ();        }} public static void Main (string[] args) {MyThread MyThread = new MyThread (); ThreAd thread1 = new Thread (myThread);        Thread thread2 = new Thread (myThread);        Thread1.start ();        Thread2.start ();            try {thread1.join ();            Thread2.join ();        System.out.println (count);        } catch (Interruptedexception e) {e.printstacktrace (); }    }}

After the code runs, the results are as follows:

1535644833489 increase begin1535644838489 increase end1535644838489 decrease begin1535644848489 decrease end

At thread1 run time, it starts with a increase function that pauses the milliseconds inside the function, 5000 during which time it is thread2 started, but waits for the increase end to enter the decrease function.

Verify that different objects are not the same as the common method lock

If the object is different, the lock is different and does not work.

In the thread that just ended up with the error, UnsafeThread Add and synchronized change the main function to verify the result.

public static void main(String[] args) {    UnsafeThread unsafeThread = new UnsafeThread();    UnsafeThread unsafeThread2 = new UnsafeThread();    Thread thread1 = new Thread(unsafeThread);    Thread thread2 = new Thread(unsafeThread2);    thread1.start();    thread2.start();    try {        thread1.join();        thread2.join();        System.out.println(count);    } catch (InterruptedException e) {        e.printStackTrace();    }}

The output will also be 4000000 smaller than the number, because count it is static decorated, is a global variable, even if it is two different object operation is the same variable. And because the unsafeThread and unsafeThread2 is two different objects, so synchronized the lock object is not the same, the lock is not the same as the effect of mutual exclusion.

Because the lock is the current object, if the objects held by two threads are not the same, they do not act mutually exclusive.

Static synchronization method, the lock is the class object of the current classes

If the synchronized function is on a static method, the lock is the current Class to lock. Because static members are not part of a specific object, it is not feasible to continue using this as a lock.

Verify that the same staticBetween methods, the lock is the same lock.

First, define two thread classes

public class ThreadA implements Runnable {    private ThreadStaticTest threadStaticTest;    public ThreadA(ThreadStaticTest threadStaticTest) {        this.threadStaticTest = threadStaticTest;    }    public void run() {        threadStaticTest.staticMethodA();    }}
public class ThreadB implements Runnable {    private ThreadStaticTest threadStaticTest;    public ThreadB(ThreadStaticTest threadStaticTest) {        this.threadStaticTest = threadStaticTest;    }    public void run() {        threadStaticTest.staticMethodB();    }}

Then, define a test class

public class Threadstatictest {public synchronized static void Staticmethoda () {System.out.println (Thread.curr        Entthread (). GetName () + "Staticmethoda in" + System.currenttimemillis ());        try {thread.sleep (2000L);        } catch (Interruptedexception e) {e.printstacktrace (); } System.out.println (Thread.CurrentThread (). GetName () + "Staticmethoda out" + System.currenttimemilli    s ()); } public synchronized static void Staticmethodb () {System.out.println (Thread.CurrentThread (). GetName () + "stat        Icmethodb in "+ System.currenttimemillis ());        try {thread.sleep (3000L);        } catch (Interruptedexception e) {e.printstacktrace (); } System.out.println (Thread.CurrentThread (). GetName () + "Staticmethodb out" + System.currenttimemilli    s ()); } public static void Main (string[] args) {threadstatictest threadstatictest = new threAdstatictest ();        Thread ta = new Thread (new Threada (threadstatictest));        Thread TB = new Thread (new THREADB (threadstatictest));        Ta.setname ("Threada");        Tb.setname ("threadb");        Ta.start ();        Tb.start ();            try {ta.join ();        Tb.join ();        } catch (Interruptedexception e) {e.printstacktrace (); }    }}

The results after the run are as follows:

ThreadA staticMethodA in 1535769147351ThreadA staticMethodA out 1535769149351ThreadB staticMethodB in 1535769149351ThreadB staticMethodB out 1535769152351

The results look nothing different. But in essence, the lock object is still not the same, the synchronized keyword is added to the static method, the lock is class, and added to the non-static method, the lock is the this object.

Verify that the static method of the same class differs from the normal method lock

First, ThreadStaticTest add a common method inside the previous class

public synchronized void methodC() {    System.out.println(Thread.currentThread().getName() + " methodC in "            + System.currentTimeMillis());    try {        Thread.sleep(3000L);    } catch (InterruptedException e) {        e.printStackTrace();    }    System.out.println(Thread.currentThread().getName() + " methodC out "            + System.currentTimeMillis());}

Defining a new Thread class

public class ThreadC implements Runnable {    private ThreadStaticTest threadStaticTest;    public ThreadC(ThreadStaticTest threadStaticTest) {        this.threadStaticTest = threadStaticTest;    }    public void run() {        threadStaticTest.methodC();    }}

Finally, add a new call to the main function

public static void main(String[] args) {    ThreadStaticTest threadStaticTest = new ThreadStaticTest();    Thread ta = new Thread(new ThreadA(threadStaticTest));    Thread tb = new Thread(new ThreadB(threadStaticTest));    Thread tc = new Thread(new ThreadC(threadStaticTest));    ta.setName("ThreadA");    tb.setName("ThreadB");    tc.setName("ThreadC");    ta.start();    tb.start();    tc.start();    try {        ta.join();        tb.join();        tc.join();    } catch (InterruptedException e) {        e.printStackTrace();    }}

The final result is as follows

ThreadA staticMethodA in 1535769547612ThreadC methodC in 1535769547612ThreadA staticMethodA out 1535769549612ThreadB staticMethodB in 1535769549612ThreadC methodC out 1535769550612ThreadB staticMethodB out 1535769552612

As you can see, the ThreadC thread named "Mess in", while the thread ThreadA and also the one after the end of the ThreadB entry. Prove ThreadA and ThreadB hold the same lock.

Synchronization code block, lock is the object inside the parentheses

The use of methods (static or Normal) synchronized , followed by a decrease in performance, in the previous proof that if multiple common methods of the same object are used synchronized , they will block each other because they hold the same lock. Therefore, it is best to use it only on methods that need to modify the shared data in the concurrency scenario .

So, how do we do it? Maybe sometimes we write a very large method, but only a small piece of it is the operation of shared data, this time in the method synchronized is obviously not cost-effective, synchronous code block can solve this kind of problem.

 synchronized(lock){    // 访问或修改共享数据 }

In a synchronized code block, synchronized the following parentheses lock can be any object .

The previous common method, corresponding to the

 synchronized(this){    // 访问或修改共享数据 }

The static method corresponds to the

    // XXX 对应的是相应的类名 synchronized(XXX.class){    // 访问或修改共享数据 }

With the use of synchronous code blocks, we can reduce performance to some extent. If there are two variables, the corresponding methods MethodA and MethodB are modified, and if you use two different lockA and lockB lock them, the operation will not block each other.

First, define the test class

public class Synblock {private Lock Locka = new Lock ();    Private lock lockb = new Lock ();    private static int CountA = 0;    private static int countb = 0; public void MethodA () {synchronized (Locka) {try {System.out.println (thread.currentthr                EAD (). GetName () + "MethodA Begin" + System.currenttimemillis ());                for (int i = 0; i < 2000000; i++) {counta++;                } thread.sleep (1000L); System.out.println (Thread.CurrentThread (). GetName () + "MethodA End" + system.currenttimemillis ())            ;            } catch (Interruptedexception e) {e.printstacktrace (); }}} public void MethodB () {synchronized (lockb) {try {System.out.printl                N (Thread.CurrentThread (). GetName () + "MethodA Begin" + System.currenttimemillis ()); For (int i = 0; i < 1000000;                i++) {countb++;                } thread.sleep (2000L); System.out.println (Thread.CurrentThread (). GetName () + "MethodA End" + system.currenttimemillis ())            ;            } catch (Interruptedexception e) {e.printstacktrace (); }}}} class Lock {} public static void Main (string[] args) {Synblock synblock = new Synblock ()        ;        Thread Threada = new Thread (new Synblockthreada (Synblock));        Thread threadb = new Thread (new SYNBLOCKTHREADB (Synblock));        Threada.setname ("Threada");        Threadb.setname ("threadb");        Threada.start ();        Threadb.start ();            try {threada.join ();            Threadb.join ();            System.out.println ("counta=" + CountA);        System.out.println ("countb=" + countb);        } catch (Interruptedexception e) {e.printstacktrace (); }    }}

The corresponding thread class

public class SynBlockThreadA implements Runnable {    private SynBlock synBlock;    public SynBlockThreadA(SynBlock synBlock) {        this.synBlock = synBlock;    }    public void run() {        synBlock.methodA();    }}
public class SynBlockThreadB implements Runnable {    private SynBlock synBlock;    public SynBlockThreadB(SynBlock synBlock) {        this.synBlock = synBlock;    }    public void run() {        synBlock.methodB();    }}

The final output results are as follows

ThreadA methodA begin 1535772725304ThreadB methodA begin 1535772725304ThreadA methodA end 1535772726319ThreadB methodA end 1535772727320countA=2000000countB=1000000

From the results can be seen ThreadA and ThreadB do not block each other.

However, if you methodB change the in lockB , the lockA result is as follows

ThreadA methodA begin 1535774917415ThreadA methodA end 1535774918431ThreadB methodA begin 1535774918431ThreadB methodA end 1535774920431countA=2000000countB=1000000

The effect of mutual exclusion.

Therefore, the following code blocks are synchronized:

When multiple threads hold the same lock (the same object in parentheses), only one thread at a time can execute synchronized code in the synchronized code block.

synchronizedThe use of this temporary end, the follow-up will be the principle of in-depth explanation and the combination of lock for more in-depth use of the explanation.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.