Copyright notice: this article is the blogger’s original article, without the permission of the blogger shall not be reproduced source: github.com/AnliaLee if you see any mistakes or good suggestions, welcome to leave a comment

preface

This is the second chapter of the Android Multithreading chapter. In the last chapter, we compared the similarities and differences between threads and Runnable threads, and also briefly simulated the multi-threading task scenario. In fact, it’s not safe to perform multithreaded tasks in this way. In this chapter, we will analyze why thread insecurity occurs and how synchronized can be used to solve this problem

Android multithreading (1) Thread and Runnable connection and difference


Synchronized usage parsing

Synchronous method (non-static)

Last time we talked about small R (Runnable) because of integrity management, business is getting better and better, so small R will recruit a salesman. One day, shortly after selling 10 tickets, R received complaints from customers who said they had bought fake tickets. Small R suspect is his hands moved hands and feet, they launched an investigation:

The business process was as follows

private class SellTask {
	private int ticket = 10;
	public void sellTicket(a){
		if (ticket > 0) {
			try{
				Thread.sleep(500);
				Log.e("R",Thread.currentThread().getName() + "Sold a ticket numbered R." + (ticket--));
			}catch(Exception e){ e.printStackTrace(); }}}}public class TicketRunnable implements Runnable {
	SellTask sellTask;
	public TicketRunnable(SellTask sellTask){
		this.sellTask = sellTask;
	}

	public void run(a) {
		for (int i = 0; i < 10; i++) { sellTask.sellTicket(); }}}Copy the code

The tickets were sold by three salesmen

SellTask sellTask = new SellTask();
TicketRunnable runnable = new TicketRunnable(sellTask);
Thread r1 = new Thread(runnable, "Salesman No. 1");
Thread r2 = new Thread(runnable, "Salesman No. 2");
Thread r3 = new Thread(runnable, "Salesman No. 3");

r1.start();
r2.start();
r3.start();
Copy the code

The investigation found that multiple salesmen had sold tickets with the same number

After further investigation, appear such circumstance is because the salesman agreed to sell to customers a certain number of votes, and received the deposit, back to get ticket to find you guys sell is with a ticket (multiple threads has Shared data) handling data error), had no choice but to copy a piece to customers in an attempt to muddle through. Small R does not know how to restrain his subordinates, so open recruitment can solve the problem

One day, a man who identified himself as Synchronized applied. Little R asked, “What does Mr. S have to say?” Mr. S calmly took a sip of his tea and replied:

“Your current business process is not reliable (thread is not safe). Let me manage the whole ticket business in a unified way. The sale of every ticket needs my approval. The problem is solved. (In Java, every object has an internal lock, and when a method is declared with the synchronized keyword, the method is protected by the object lock, so that only one thread can enter the method at a time and acquire the lock on the object, and other threads queue up to call the method. When the thread that acquired the lock finishes executing the method and releases the object lock, another thread can acquire the lock and enter the method.

After hearing this, little R felt that this method was good and let Mr. S try it. This time, 10 tickets will still be sold. The business process is improved by Mr. S as follows

private class SellTask {
	private int ticket = 10;
	public synchronized void sellTicket(a){// declare the sellTicket method with synchronized
		if (ticket > 0) {
			try{
				Thread.sleep(500);
				Log.e("R",Thread.currentThread().getName() + "Sold a ticket numbered R." + (ticket--));
			}catch(Exception e){ e.printStackTrace(); }}}}Copy the code

As expected, the problem was solved, and small R’s heart was finally put down


Multiple synchronized methods within the same object

One day, small R began to complain to Mr. S: “My gang of two leng men ah, every time into my office to report the work is messy, let them come one by one is not listen to, Mr. S think how to manage them ah?” Mr. S calmly sipped his tea and said, “No hurry. Let me see what they report first.” Small R then according to the meaning of Mr. S arranged two men to report work

private class ReportTask {
	public void report1(a){
		Log.e("R"."Salesman No. 1" + "Into the office");
		try{
			Log.e("R"."Salesman No. 1" + "Start reporting.");
			Thread.sleep(1000);

		}catch (Exception e){
			e.printStackTrace();
		}
		Log.e("R"."Salesman No. 1" + "All reported.");
		Log.e("R"."Salesman No. 1" + "Get out of the office");
	}

	public void report2(a){
		Log.e("R"."Salesman No. 2" + "Into the office");
		try{
			Log.e("R"."Salesman No. 2" + "Start reporting.");
			Thread.sleep(1000);

		}catch (Exception e){
			e.printStackTrace();
		}
		Log.e("R"."Salesman No. 2" + "All reported.");
		Log.e("R"."Salesman No. 2" + "Get out of the office"); }}public class ReportRunnable1 implements Runnable {
	ReportTask task;
	public ReportRunnable1(ReportTask task){
		this.task = task;
	}

	public void run(a) { task.report1(); }}public class ReportRunnable2 implements Runnable {
	ReportTask task;
	public ReportRunnable2(ReportTask task){
		this.task = task;
	}

	public void run(a) { task.report2(); }}Copy the code

After a while, two of his men entered the office

ReportTask reportTask = new ReportTask();
ReportRunnable1 runnable1 = new ReportRunnable1(reportTask);
ReportRunnable2 runnable2 = new ReportRunnable2(reportTask);

Thread r1 = new Thread(runnable1);
Thread r2 = new Thread(runnable2);
r1.start();
r2.start();
Copy the code

R rubbed his head and sighed, “Well, that’s how they report it. I don’t know who to listen to when they talk together.” Mr. S laughed and said:

“It’s easy to fix. The next time they report back, I lock the door on the first person who comes in, let the next person wait outside the door, and let the second one in after the first one has talked. Access by other threads to all other synchronized methods in the object will be blocked.)”

Small R after listening to the deep thought, and then arrange just those two salesman to report again, this time by Mr. S himself guarding the door (don’t ask me why they silly, computer is so work.

private class ReportTask {
	public synchronized void report1(a){
		Log.e("R"."Salesman No. 1" + "Into the office");
		try{
			Log.e("R"."Salesman No. 1" + "Start reporting.");
			Thread.sleep(1000);

		}catch (Exception e){
			e.printStackTrace();
		}
		Log.e("R"."Salesman No. 1" + "All reported.");
		Log.e("R"."Salesman No. 1" + "Get out of the office");
	}

	public synchronized void report2(a){
		Log.e("R"."Salesman No. 2" + "Into the office");
		try{
			Log.e("R"."Salesman No. 2" + "Start reporting.");
			Thread.sleep(1000);

		}catch (Exception e){
			e.printStackTrace();
		}
		Log.e("R"."Salesman No. 2" + "All reported.");
		Log.e("R"."Salesman No. 2" + "Get out of the office"); }}Copy the code

In a short while, two salesman came again, this time the result makes small R very satisfied

Seeing little R so happy, Mr. S couldn’t help splashing cold water:

“Don’t get too excited. Your window is unlocked, and your bunch of idiots can’t get through it (when one thread accesses a synchronized method, another thread can still access a non-synchronized method).”

Sure enough, in a later work report, such a thing happened

private class ReportTask {
	public synchronized void report1(a){
		Log.e("R"."Salesman No. 1" + "Into the office");
		try{
			Log.e("R"."Salesman No. 1" + "Start reporting.");
			Thread.sleep(1000);

		}catch (Exception e){
			e.printStackTrace();
		}
		Log.e("R"."Salesman No. 1" + "All reported.");
		Log.e("R"."Salesman No. 1" + "Get out of the office");
	}

	public synchronized void report2(a){
		Log.e("R"."Salesman No. 2" + "Into the office");
		try{
			Log.e("R"."Salesman No. 2" + "Start reporting.");
			Thread.sleep(1000);

		}catch (Exception e){
			e.printStackTrace();
		}
		Log.e("R"."Salesman No. 2" + "All reported.");
		Log.e("R"."Salesman No. 2" + "Get out of the office");
	}

	public void report3(a){
		Log.e("R"."Salesman No. 3" + "Into the office");
		try{
			Log.e("R"."Salesman No. 3" + "Start reporting.");
			Thread.sleep(1000);

		}catch (Exception e){
			e.printStackTrace();
		}
		Log.e("R"."Salesman No. 3" + "All reported.");
		Log.e("R"."Salesman No. 3" + "Get out of the office"); }}// Thread start code...
Copy the code

R:


Synchronized code block

That day, friend small T came to visit small R, but saw small R’s office doors and Windows closed, several salesmen lined up outside the door. Small T is very confused, then knock on the door and beckon small R to let him open the door, but did not get any response. Little T had to wait outside the office with the salesman

private class ReportTask {
	public void report1(a){
		synchronized(this){
			Log.e("R"."Salesman No. 1" + "Into the office");
			try{
				Log.e("R"."Salesman No. 1" + "Start reporting.");
				Thread.sleep(1000);

			}catch (Exception e){
				e.printStackTrace();
			}
			Log.e("R"."Salesman No. 1" + "All reported.");
			Log.e("R"."Salesman No. 1" + "Get out of the office"); }}public void report2(a){
		synchronized(this){
			Log.e("R"."Salesman No. 2" + "Into the office");
			try{
				Log.e("R"."Salesman No. 2" + "Start reporting.");
				Thread.sleep(1000);

			}catch (Exception e){
				e.printStackTrace();
			}
			Log.e("R"."Salesman No. 2" + "All reported.");
			Log.e("R"."Salesman No. 2" + "Get out of the office"); }}public void report3(a){
		synchronized(this){
			Log.e("R"."Salesman No. 3" + "Into the office");
			try{
				Log.e("R"."Salesman No. 3" + "Start reporting.");
				Thread.sleep(1000);

			}catch (Exception e){
				e.printStackTrace();
			}
			Log.e("R"."Salesman No. 3" + "All reported.");
			Log.e("R"."Salesman No. 3" + "Get out of the office"); }}public void report4(a){
		synchronized (this){
			Log.e("R"."T" + "Into the office"); }}}Copy the code
ReportTask reportTask = new ReportTask();
ReportRunnable1 runnable1 = new ReportRunnable1(reportTask);
ReportRunnable2 runnable2 = new ReportRunnable2(reportTask);
ReportRunnable3 runnable3 = new ReportRunnable3(reportTask);
ReportRunnable4 runnable4 = new ReportRunnable4(reportTask);

Thread s1 = new Thread(runnable1);
Thread s2 = new Thread(runnable2);
Thread s3 = new Thread(runnable3);
Thread s4 = new Thread(runnable4);
s1.start();
s2.start();
s3.start();
s4.start();
Copy the code

It is not easy to wait until the door opened, a salesman walked out, small T will slip in, the door was immediately locked by small R. “What were you doing? You almost clipped me!” T complained. Small R embarrassed smile, say: “the original is small T, you sit you sit, wait to explain to you again, I first put down a salesman come in…”

It is not easy to deal with all the salesman, small R to small T explained the context, and took out a key to small T

private class ReportTask {
	public void report1(a){
		synchronized(this) {// omit some code...}}public void report2(a){
		synchronized(this) {// omit some code...}}public void report3(a){
		synchronized(this) {// omit some code...}}private String window = "window";
	public void report4(a){
		synchronized (window){
			Log.e("R"."T" + "Into the office"); }}}Copy the code

“Mr. S, of course, has a solution for this. This key opens the window (which has a built-in lock on the window object) and allows you to climb in without having to queue up outside.” synchronized (obj){} Both of them lock codes within the scope of synchronized. The difference lies in the fact that synchronized synchronized code blocks are more flexible and light in use. The object parameters in the brackets of synchronized (obj){} are the objects that the code blocks hold locks. For example, the synchronization block in the previous three Report methods holds the lock on the ReportTask instance object, while the synchronization block in the Report4 method holds the lock on the Window object. Because each object has its own object lock, it can only protect its own synchronized code block or synchronized method, so if another thread enters the synchronized code block of the first three methods and obtains the lock of the corresponding object, it will not block the thread entering the Report4 method from executing the synchronized code.

After getting the key, T no longer had to wait in line outside the door with the salesman


Statically synchronized method

With that in mind, let’s go back to understanding the difference between synchronous and statically synchronized methods. The synchronized approach to code can be divided into two main factions based on which objects hold the lock:

  • Synchronized (this){} and synchronized (not this object){} declare non-static methods and blocks of synchronized code. A thread that wants to execute synchronized code in the scope of synchronized acquires an object lock
public class SynchronizedTest {
    public synchronized void test1(a){
        // The object holding the lock is an instance of SynchronizedTest
    }

    public void test2(a){
        synchronized (this) {// The object holding the lock is an instance of SynchronizedTest}}private String obj = "obj";
    public void test3(a){
        synchronized (obj){
            // The lock object is obj}}}Copy the code
  • Synchronized (class.class){} Both declare static methods and synchronized (class.class){} hold the lock on class objects. (Each class has only one class object, and class objects are generated by the compilation of Java classes. A thread that wants to execute synchronized code in the scope of synchronized acquires a class lock
public class SynchronizedTest {
    public static synchronized void test4(a){
        SynchronizedTest Class object (synchronizedtest.class)
    }

    public void test5(a){
        synchronized (SynchronizedTest.class){
            SynchronizedTest Class object (synchronizedtest.class)}}}Copy the code

The details of instance objects and Class objects can be found on the Internet, but I won’t go into them here, but one thing to keep in mind

If the synchronized synchronization method (code block) holds the lock of different objects, then the multi-thread execution of the corresponding synchronization code does not interfere with each other; If they are the same, the line that acquired the lock executes the synchronization code first, and other threads accessing the synchronization code block and wait for the lock to be released


update

To better understand the difference between static and non-static synchronized methods, I’ll add an example here

In a parallel universe, small R b through to the world, in order to in the village to get a tight, hence decided to please the village of nights people (ontology, the Class object) and his shadow copy (instance objects) to eat ramen (shadow copy can also be understood as a clone, reality didn’t find any good example can plug in, everybody will ha ha). In order to find Naruto’s scattered shadow incarnations, R also uses shadow incarnations

public class SynchronizedTest {
    public synchronized void test1(a){
        try{
            System.out.println("Two shadows, please."+Thread.currentThread().getName()+"Eat ramen noodles");
            Thread.sleep(1000);

        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("A shadow and a shadow."+Thread.currentThread().getName()+"I'm done with ramen.");
    }

    public static void main(String args[]) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run(a) {
                newSynchronizedTest().test1(); }},"1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run(a) {
                newSynchronizedTest().test1(); }},"2");
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run(a) {
                newSynchronizedTest().test1(); }},"3"); t1.start(); t2.start(); t3.start(); }}Copy the code

The unstatically synchronized methods are called as new SynchronizedTest().xxx, so they hold different locks and therefore have no effect on each other.

As for Naruto himself, it must focus on “bribery”, so R sent three shadow incarnations to treat him to eat ramen

public class SynchronizedTest {
    public static synchronized void test2(a){
        try{
            System.out.println("Naruto to eat ramen:" + Thread.currentThread().getName());
            Thread.sleep(1000);

        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("Naruto finishes ramen:" + Thread.currentThread().getName());
    }

    public static void main(String args[]) {
        Thread t4 = new Thread(new Runnable() {
            @Override
            public void run(a) {
// test2(); Test2 methods are static and can be called directly
                new SynchronizedTest().test2();// This is called only for comparison}},"t4");
        Thread t5 = new Thread(new Runnable() {
            @Override
            public void run(a) {
                newSynchronizedTest().test2(); }},"t5");
        Thread t6 = new Thread(new Runnable() {
            @Override
            public void run(a) {
                newSynchronizedTest().test2(); }},"t6"); t4.start(); t5.start(); t6.start(); }}Copy the code

There is only one Naruto ontology, even if three people are invited together, they will eat one bowl after another. (In the static synchronization method, all the objects holding the lock are Class objects, and the thread holding the lock normally performs the task, while other threads without the lock will block and wait.)

Synchronized (this){}, synchronized (XXX. Class){}, synchronized (XXX. Class){}, synchronized (XXX. Classes, Instances, and Class objects in JAVA. If you have any questions, please leave a comment, shang ban will come to reply one by one