A thorough understanding of synchronized in one article (easy-to-understand synchronized)

Table of contents

1. What is synchronized

2. Four uses of synchronized

2.1. Modify a code block

2.2. Modify a method

2.3. Modify a static method

2.4. Modify a class

3. Use case analysis

3.1. Modify a code block

3.2. Modify a method

3.3. Modify a static method

3.4. Modify a class

3.5 Classic usage:

Summarize


 

1. What is synchronized

synchronized is a keyword in Java and is a synchronization lock. It is mainly used to ensure thread safety in multi-threaded environments.

2. Four uses of synchronized

2.1. Modify a code block

         The modified code block is called a synchronized statement block, and its scope is the code enclosed by curly brackets {}, and the object it acts on is the object that calls this code block ;

2.2. Modify a method

        The modified method is called a synchronized method, its scope is the entire method, and the object it acts on is the object that calls this method;

        Although you can use synchronized to define methods, synchronized is not part of the method definition, so the synchronized keyword cannot be inherited. If a method in the parent class uses the synchronized keyword and the method is overridden in the subclass, the method in the subclass is not synchronized by default and must be explicitly specified in the subclass. Just add the synchronized keyword to the method. Of course, you can also call the corresponding method in the parent class in the subclass method. In this way, although the method in the subclass is not synchronous, the subclass calls the synchronization method of the parent class. Therefore, the method of the subclass is equivalent to synchronization. .

2.3. Modify a static method

Its scope of action is the entire static method, and its objects are all objects of this class ;

2.4. Modify a class

Its scope is the part enclosed in brackets after synchronized, and its scope is all objects of this class.

3. Use case analysis

3.1. Modify a code block

1) When a thread accesses the synchronized(this) synchronized code block in an object, other threads trying to access the object will be blocked.

class SyncThread implements Runnable {
	   private static int count;
 
	   public SyncThread() {
	      count = 0;
	   }
 
	   public  void run() {
	      synchronized(this) {
	         for (int i = 0; i < 5; i++) {
	            try {
	               System.out.println(Thread.currentThread().getName() + ":" + (count++));
	               Thread.sleep(100);
	            } catch (InterruptedException e) {
	               e.printStackTrace();
	            }
	         }
	      }
	   }
 
	   public int getCount() {
	      return count;
	   }
}
 
public class SynchronizedDemo {
	public static void main(String args[]){
//test01
//		SyncThread s1 = new SyncThread();
//		SyncThread s2 = new SyncThread();
//		Thread t1 = new Thread(s1);
//		Thread t2 = new Thread(s2);
//test02		
		SyncThread s = new SyncThread();
		Thread t1 = new Thread(s);
		Thread t2 = new Thread(s);
		
		t1.start();
		t2.start();
	}
}

Test01

test02

From the running result test02, we can see that when two concurrent threads (thread1 and thread2) access the synchronized code block in the same object (syncThread), only one thread can be executed at the same time, and the other thread is blocked and must wait. This code block cannot be executed until the current thread has finished executing this code block. Thread1 and thread2 are mutually exclusive, because the current object will be locked when the synchronized code block is executed. Only when the code block is executed can the object lock be released, and the next thread can execute and lock the object.

Why are thread1 and thread2 executing at the same time in the above example? This is because synchronized only locks objects, and each object has only one lock associated with it.

2) When a thread accesses a synchronized(this) synchronized code block of an object, another thread can still access the non-synchronized(this) synchronized code block in the object. (Assignment: Self-verification)

3) Specify an object to be locked

package CompleteFuture;


/**
 * 银行账户类
 */
class Account {
    String name;
    float amount;

    public Account(String name, float amount) {
        this.name = name;
        this.amount = amount;
    }
    //存钱
    public  void deposit(float amt) {
        amount += amt;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //取钱
    public  void withdraw(float amt) {
        amount -= amt;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public float getBalance() {
        return amount;
    }
}

/**
 * 账户操作类
 */
class AccountOperator implements Runnable{
    private Account account;
    public AccountOperator(Account account) {
        this.account = account;
    }

    public void run() {
        synchronized (account) {
            account.deposit(500);
            account.withdraw(500);
            System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
        }
    }
}


public class SynchDemo2 {
    //public static final Object signal = new Object(); // 线程间通信变量
    //将account改为Demo00.signal也能实现线程同步
    public static void main(String args[]){
        Account account = new Account("zhang san", 10000.0f);
        AccountOperator accountOperator = new AccountOperator(account);

        final int THREAD_NUM = 5;
        Thread threads[] = new Thread[THREAD_NUM];
        for (int i = 0; i < THREAD_NUM; i ++) {
            threads[i] = new Thread(accountOperator, "Thread" + i);
            threads[i].start();
        }
    }
}

In the run method in the AccountOperator class, we use synchronized to lock the account object. At this time, when a thread accesses the account object, other threads trying to access the account object will be blocked until the thread accesses the account object. In other words, whoever gets the lock can run the code it controls. 


3.3.1 When there is a clear object as a lock, you can write a program in a way similar to the following.

public void method3(SomeObject obj)
{
   //obj 锁定的对象
   synchronized(obj)
   {
      // todo
   }
}

3.3.2 When there is no explicit object as a lock and you just want to synchronize a piece of code, you can create a special object to act as a lock:

class Test implements Runnable
{
   private byte[] lock = new byte[0];  // 特殊的instance变量
   public void method()
   {
      synchronized(lock) {
         // todo 同步代码块
      }
   }
 
   public void run() {
 
   }
}

3.2. Modify a method

public void method()
{
   synchronized(this) {
      // todo
   }
}

Add the synchronized keyword to the subclass method

class Parent {
   public synchronized void method() { }
}
class Child extends Parent {
   public synchronized void method() { }
}

Call the synchronized method of the parent class in the subclass method

class Parent {
   public synchronized void method() {   }
}
class Child extends Parent {
   public void method() { super.method();   }
} 
  1. The synchronized keyword cannot be used when defining interface methods.
  2. The constructor cannot use the synchronized keyword, but can use synchronized code blocks for synchronization.

3.3. Modify a static method

/**
 * 同步线程
 */
class SyncThread implements Runnable {
   private static int count;
 
   public SyncThread() {
      count = 0;
   }
 
   public synchronized static void method() {
      for (int i = 0; i < 5; i ++) {
         try {
            System.out.println(Thread.currentThread().getName() + ":" + (count++));
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
 
   public synchronized void run() {
      method();
   }
}
 
public class Demo00{
	
	public static void main(String args[]){
		SyncThread syncThread1 = new SyncThread();
		SyncThread syncThread2 = new SyncThread();
		Thread t1 = new Thread(syncThread1, "SyncThread1");
		Thread t2 = new Thread(syncThread2, "SyncThread2");
		t1.start();
		t2.start();
	}
}

syncThread1 and syncThread2 are two objects of SyncThread, but they maintain thread synchronization when t1 and t2 are executed concurrently. This is because the static method method is called in run, and the static method belongs to the class, so syncThread1 and syncThread2 are equivalent to using the same lock.

3.4. Modify a class

Its scope is the part enclosed in brackets after synchronized, and its scope is all objects of this class.

/**
 * 同步线程
 */
class SyncThread implements Runnable {
   private static int count;
 
   public SyncThread() {
      count = 0;
   }
 
   public static void method() {
      synchronized(SyncThread.class) {
         for (int i = 0; i < 5; i ++) {
            try {
               System.out.println(Thread.currentThread().getName() + ":" + (count++));
               Thread.sleep(100);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }
 
   public synchronized void run() {
      method();
   }
}

3.5 Classic usage:

consumers and producers

package CompleteFuture;

import java.util.Random;

public class ProducerAndConsumer {
    public static void main(String[] args) {

        //多线程如何编写
        // 1、线程操作资源类
        // 2、创建资源类 ,在资源类中创建属性和操作资源方法
        Product product = new Product();
        for (int i = 1; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    product.product();
                }
            },"生产者: "+i).start();
        }

        
        for (int i = 1; i <10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    product.consume();
                }
            },"消费者: "+i).start();
        }
    }
    
}

// 资源类
class Product{

    private volatile int num = 0;
    
    public synchronized void  product(){

        // 1、馒头有的多我就可以不生产
        while (num !=0){

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 一次生产五个
        for (int i = 0; i <5 ; i++) {
            ++num;
        }
        
        // 产生了馒头通知消费者
        this.notifyAll();
        System.out.println(Thread.currentThread().getName()+"生产后剩余馒头:"+num);

    }


    public synchronized void  consume(){

        // 1、如果没有馒头我就等待,阻塞消费
        while (num  == 0){

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 一次吃2个
        for (int i = 0; i < 2; i++) {
            if(num>0) {
                --num;
            }
        }

        // 模拟消耗馒头1s
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.notifyAll();
        System.out.println(Thread.currentThread().getName()+"消费后剩余馒头:"+num);
    }
}

Singleton mode (double detection):


 


Summarize

A. Regardless of whether the synchronized keyword is added to a method or an object, if the object it acts on is non static , the lock it acquires is the object ; if the object synchronized acts on is a static method or a class , the lock it acquires It is a class, and all objects of this class have the same lock. 
B. Each object has only one lock associated with it. Whoever gets this lock can run the code it controls. 
C. Achieving synchronization requires a lot of system overhead and may even cause deadlock, so try to avoid unnecessary synchronization control.


This article mainly summarizes the main usage of synchronized, but its actual principle is not dismantled in detail. Let’s look at the analysis next time.

Guess you like

Origin blog.csdn.net/u010445301/article/details/133154016