Home  >  Article  >  Java  >  How to use Synchronized in Java

How to use Synchronized in Java

高洛峰
高洛峰Original
2016-12-13 11:04:221712browse

synchronized is a keyword in Java and is a kind of synchronization lock. The objects it modifies are as follows:
1. Modify a code block. The modified code block is called a synchronization statement block. Its scope of action is the code enclosed in curly brackets {}, and the object of action is to call this code block. Object;
2. Modify a method. The modified method is called a synchronization method. Its scope is the entire method, and the object it works on is the object that calls this method;
3. Modify a static method, and its scope is It is the entire static method, and the objects it acts on are all objects of this class;
4. When modifying a class, its scope is the part enclosed in parentheses after synchronized, and the main objects it acts on are all objects of this class.

Modify a code block

When a thread accesses the synchronized(this) synchronized code block in an object, other threads trying to access the object will be blocked. Let’s look at the following example:

[Demo1]: usage of synchronized

/**
 * 同步线程
 */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;
   }
}

Calling SyncThread:

SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "SyncThread1");
Thread thread2 = new Thread(syncThread, "SyncThread2");
thread1.start();
thread2.start();

The result is as follows:

SyncThread1:0 
SyncThread1:1 
SyncThread1:2 
SyncThread1:3 
SyncThread1:4 
SyncThread2:5 
SyncThread2:6 
SyncThread2:7 
SyncThread2:8 
SyncThread2:9*

When two concurrent threads (thread1 and thread2) access the same object (syncThread) When a synchronized code block is used, only one thread can be executed at the same time, and the other thread is blocked and must wait for the current thread to finish executing the code block before the code block can be executed. Thread1 and thread2 are mutually exclusive, because the current object is locked when the synchronized code block is executed. Only after the code block is executed can the object lock be released, and the next thread can execute and lock the object.
Let’s slightly change the call to SyncThread:

Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
thread1.start();
thread2.start();

The result is as follows:

SyncThread1:0 
SyncThread2:1 
SyncThread1:2 
SyncThread2:3 
SyncThread1:4 
SyncThread2:5 
SyncThread2:6 
SyncThread1:7 
SyncThread1:8 
SyncThread2:9

Didn’t it mean that when one thread executes the synchronized code block, other threads are blocked? 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 (lock) associated with it, and the above code is equivalent to the following code:

SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();

At this time, two SyncThread objects, syncThread1 and syncThread2, are created. Thread1 executes the synchronized code (run) in the syncThread1 object, and thread thread2 executes the synchronized code (run) in the syncThread2 object; we know that synchronized locks the object, and there will be two locks locking the syncThread1 object and syncThread2 object, and these two locks do not interfere with each other and do not form mutual exclusion, so the two threads can execute at the same time.

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.
[Demo2]: Multiple threads access synchronized and non-synchronized code blocks

class Counter implements Runnable{
   private int count;   public Counter() {      count = 0;
   }   public void countAdd() {
      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();
            }
         }
      }
   }   //非synchronized代码块,未对count进行读写操作,所以可以不用synchronized
   public void printCount() {      for (int i = 0; i < 5; i ++) {         try {
            System.out.println(Thread.currentThread().getName() + " count:" + count);
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }   public void run() {
      String threadName = Thread.currentThread().getName();      if (threadName.equals("A")) {
         countAdd();
      } else if (threadName.equals("B")) {
         printCount();
      }
   }
}

Calling code:

Counter counter = new Counter();
Thread thread1 = new Thread(counter, "A");
Thread thread2 = new Thread(counter, "B");
thread1.start();
thread2.start();

The results are as follows:

A:0 
B count:1 
A:1 
B count:2 
A:2 
B count:3 
A:3 
B count:4 
A:4 
B count:5

In the above code, countAdd is synchronized, and printCount is non-synchronized. It can be seen from the above results that when a thread accesses the synchronized code block of an object, other threads can access the non-synchronized code block of the object without being blocked.

Specify to lock an object

[Demo3]: Specify to lock an object

/**
 * 银行账户类
 */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());
      }
   }
}

Calling code:

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();
}

The result is as follows:

Thread3:10000.0 
Thread2:10000.0 
Thread1:10000.0 
Thread4:10000.0 
Thread0:10000.0

In the run method in the AccountOperator class, we use synchronized adds a lock to 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.
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
   }
}

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() {

   }
}

Explanation: A zero-length byte array object will be more economical to create than any object - Looking at the compiled bytecode: generating a zero-length byte[] object requires only 3 opcodes, while Object lock = new Object() requires 7 lines of opcodes.

Modify a method

Synchronized Modifying a method is very simple, just add synchronized in front of the method, public synchronized void method(){//todo}; The synchronized modification method is similar to modifying a code block, but the scope is different. The modified code block is the scope enclosed by curly braces, while the modified method scope is the entire function. If you change the run method in [Demo1] to the following method, the effect will be the same.

*[Demo4]: synchronized modifies a method

public synchronized void run() {   for (int i = 0; i < 5; i ++) {      try {
         System.out.println(Thread.currentThread().getName() + ":" + (count++));
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
}

Synchronized acts on the entire method.
Writing method one:

public synchronized void method()
{
   // todo
}

Writing method two:

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

Writing method one modifies a method, and writing method two modifies a code block, but writing method one and writing method two are equivalent, and they both lock the content of the entire method. .

在用synchronized修饰方法时要注意以下几点: 
1. synchronized关键字不能继承。 
虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。这两种方式的例子代码如下: 
在子类方法中加上synchronized关键字

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

在子类方法中调用父类的同步方法

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

在定义接口方法时不能使用synchronized关键字。

构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。 

修饰一个静态的方法

Synchronized也可修饰一个静态方法,用法如下:

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

我们知道静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。我们对Demo1进行一些修改如下:

【Demo5】:synchronized修饰静态方法

/**
 * 同步线程
 */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();
   }
}

调用代码:

SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();

结果如下:

SyncThread1:0 
SyncThread1:1 
SyncThread1:2 
SyncThread1:3 
SyncThread1:4 
SyncThread2:5 
SyncThread2:6 
SyncThread2:7 
SyncThread2:8 
SyncThread2:9

syncThread1和syncThread2是SyncThread的两个对象,但在thread1和thread2并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以syncThread1和syncThread2相当于用了同一把锁。这与Demo1是不同的。

修饰一个类

Synchronized还可作用于一个类,用法如下:

class ClassName {
   public void method() {
      synchronized(ClassName.class) {
         // todo
      }
   }
}

我们把Demo5再作一些修改。 
【Demo6】:修饰一个类

/**
 * 同步线程
 */
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();
   }
}

其效果和【Demo5】是一样的,synchronized作用于一个类T时,是给这个类T加锁,T的所有对象用的是同一把锁。

总结:

A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。 
B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。 
C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn