Synchronized keyword

  synchronized , adjective, means synchronized. It is often used in multithreading. We often encounter multiple threads accessing the same shared resource. At this time, we must consider how to maintain data consistency. In java, the synchronized keyword is often used to maintain data consistency. The synchronized mechanism locks the shared resources. Only the thread that gets the lock can access the shared resources, so that the access to the shared resources can be forced to be sequential.

1. The concept of lock

  Because the synchronized keyword involves the concept of locks, let's first understand some related lock knowledge. Every java object can be used as a lock to achieve synchronization, these locks become built-in locks. The only way to acquire a built-in lock is to enter the synchronized code block or method protected by the lock. The thread automatically acquires the lock when it enters the synchronized code block or method, and releases the lock when it exits the synchronized code block or method (whether by whether the statement exited normally or an exception was thrown during execution). As mentioned earlier, access to shared resources must be sequential, that is, when multiple threads access a shared resource, only one thread can obtain the lock on the shared resource. When thread A tries to acquire the lock of thread B, thread A must wait or block until thread B releases the lock, otherwise thread A will keep waiting, so java built-in lock is also called mutex lock, which means that the lock actually The above is a mutual exclusion mechanism.

  According to the different ways of use, we generally divide locks into object locks and class locks. The two locks are very different. Object locks act on instance methods or an object instance, while class locks act on static methods. Or above the Class object. A class can have multiple instance objects, so there may be multiple object locks for a class, but each class has only one Class object, so there is only one class lock. Class lock is just a conceptual thing, not real, it is only used to help us understand the difference between instance methods and static methods.

2, synchronized modified object

2.1. Decorating code blocks

The modified code block is called a synchronized statement block, and the object of action is the object that calls this code block, which is equivalent to an object lock . When a thread accesses the synchronized(this) synchronized code block in an object, other threads trying to access the object will be blocked.

ps: Please pay attention to the font marked in red and understand it correctly.

package sync;

public class SyncThread implements Runnable {
   private int count;
   public SyncThread() {
      count = 1;
   }

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

Test call:

package sync;

public class Test {

    public static void main(String[] args) {
        SyncThread th  = new SyncThread();
        
        new Thread(th,"Thread1").start();
        new Thread(th,"Thread2").start();
        
        //new Thread(new SyncThread(),"Thread3").start();
        //new Thread(new SyncThread(),"Thread4").start();
    }

}

此时线程1、2调用的是同个对象,线程2在执行,线程1一直在等待,直到线程2运行结束,结果:

 

对于Thread3和Thread4,调用的是两个对象,互不干扰,并行执行,结果:

2.2 修饰方法

2.2.1 修饰一个普通方法

被修饰的方法称为同步方法,作用的对象是调用这个方法的对象。Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,public synchronized void method(){//方法体}; synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。这里不再举例子,只需要将上面的synchronized放到run方法上。

2.2.2 修饰一个静态的方法

其作用的范围是整个静态方法,作用的对象是这个类的所有对象,因为静态方法是属于类的而不属于对象的,相当于在类上加锁。

package sync;

public class SyncThread implements Runnable {
    private static int count;
    public SyncThread() {
        count = 1;
    }

    public void run() {
        method();
    }

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

调用测试:

package sync;

public class Test {

    public static void main(String[] args) {
        SyncThread th  = new SyncThread();
        
        //new Thread(th,"Thread1").start();
        //new Thread(th,"Thread2").start();
        
        new Thread(new SyncThread(),"Thread3").start();
        new Thread(new SyncThread(),"Thread4").start();
    }
}

结果:

2.3修饰一个类

其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象,是类锁,类的所有对象用的是同一把锁。

package sync;

 class SyncThread implements Runnable {
    private int count;
    public SyncThread() {
        count = 1;
    }

    public void run() {
        method();
    }

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

测试调用:

package sync;

public class Test {

    public static void main(String[] args) {
        SyncThread th  = new SyncThread();
        
        //new Thread(th,"Thread1").start();
        //new Thread(th,"Thread2").start();
        
        new Thread(new SyncThread(),"Thread3").start();
        new Thread(new SyncThread(),"Thread4").start();
    }
}

结果:

2.4修饰一个对象

其作用的范围是synchronized后面括号括起来的部分,作用的对象是synchronized加锁的那个对象,相当于对象锁。

账户类:

package sync;

public class Account {
   String name;
   float amount;

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

   public float getBalance() {
      return amount;
   }
}

操作类:

package sync;

public class AccountOperator implements Runnable {
    
    private Account account; 
    
    public AccountOperator(Account account) {
        this.account = account;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":start");
        synchronized(account){
            account.add(5000);
            account.minus(5000);
            System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
        }
    }

}

测试类:

package sync;

public class Test {

    public static void main(String[] args) {
        Account account = new Account("zhang san", 10000.0f);
        AccountOperator accountOperator = new AccountOperator(account);

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

结果:

可以看出来线程执行synchronized(account){}代码块时都是互斥的。还可以看出此时用的是栈存储的堵塞进程。

当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:

class Test implements Runnable
{
   Object object= new Object();  // 特殊的对象
   {
      synchronized(object) {
         // todo 同步代码块
      }
   }

   public void run() {

   }
}

3、总结

1,synchronized关键字加在方法、对象或者是代码块上,如果它作用的对象是非静态的,则它取得的锁是针对对象。

2,synchronized作用的对象是一个静态方法或者是类,则它取得的锁是针对类,该类所有的对象同一把锁。

 

参考资料:https://blog.csdn.net/luoweifu/article/details/46613015

Guess you like

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