Multi-threaded programming (five) - Synchronized Keyword Detailed

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/swadian2008/article/details/99328700

table of Contents

First, the security thread variables

1, variables within the method is thread-safe

2, the external variable method is non-thread-safe

Two, Synchronized methods and locked object

1, a plurality of lock multiple objects

2, Synchronized modification methods (object lock locks)

3, Synchronized lock synchronization and do not have re-entrant inheritance

(1) reentrant lock

(2) the synchronization does not have inheritance

4, Synchronized sync block of statements, any object monitor

4.1 Data type String constant pool characteristics

5, the static method and synchronization Synchronized Synchronized (class) code blocks, Locks

5.1 difference static and non-static method Synchronized Synchronized method

6, Synchronized keyword and inner classes and static inner classes


First, the security thread variables

1, variables within the method is thread-safe

java at runtime, the data area, the virtual machine stack or native method stacks (memory area where the execution method) is a private thread, each thread has its own stack virtual machines, storage areas and native method stacks, so the thread execution when the method, the internal method does not involve variable share this principle, thread-safety issues involved will only shared memory, memory outside of the heap variable method.

2, the external variable method is non-thread-safe

Dirty read will appear in the case of operating the instance variables (external variables method), the problem is not thread-safe instance variables of competition caused by different threads.

Two, Synchronized methods and locked object

1, a plurality of lock multiple objects

Two threads are accessing the same name in two different instances of the same class Synchronized synchronization method is performed asynchronously. This is because the lock is an object lock Synchronized acquired multiple objects to have multiple locks, mutual non-interference. So make the thread synchronization, provided that multiple threads access the same object.

2, Synchronized modification methods (object lock locks)

(1) create an object MyService

public class MyService {

    // synchronized修饰的同步方法
    public synchronized void serviceA(){
        try {
            System.out.println(Thread.currentThread().getName()+"serviceA方法开始...");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+"serviceA方法结束...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void serviceB(){
        try {
            System.out.println(Thread.currentThread().getName()+"serviceB方法开始...");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+"serviceB方法结束...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

(2) creates an execution thread A and B

A thread

public class MyThreadA extends Thread {

    public MyService myService;

    public MyThreadA(MyService myService){
        super();
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.serviceA();
    }

    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThreadA myThreadA = new MyThreadA(myService);
        myThreadA.setName("A线程");
        MyThreadB myThreadB = new MyThreadB(myService);
        myThreadB.setName("B线程");
        myThreadA.start();
        myThreadB.start();
    }
}

Thread B

public class MyThreadB extends Thread {

    public MyService myService;

    public MyThreadB(MyService myService){
        super();
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.serviceB();
    }

}

Results of the:

The results 1: A thread to hold the object lock MyService, B thread can call non-Synchronized method MyService objects asynchronously.

At this modification method MyService object synchronization method serviceB

public synchronized void serviceB(){
        // 此时方法B也设为同步
        try {
            System.out.println(Thread.currentThread().getName()+"serviceB方法开始...");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+"serviceB方法结束...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

Results of the:

A first thread to wait while holding the object lock MyService, B thread then MyService object Synchronized type of method invocation, that is, in order to perform synchronization: 2 results.

3, Synchronized lock synchronization and do not have re-entrant inheritance

(1) reentrant lock

Reentrant lock known recursive lock means when the same thread acquires the lock of the outer layer method, the method will automatically get into the inner lock. Reentrant lock role is to some extent avoid deadlock.

Test code:

Creating objects MyService

public class MyService {

    public int i = 10;

    public synchronized void service() {
        try {
            i--;
            System.out.println("父类操作数:i=" + i);
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Create a child object of MyService

public class Child_Service extends MyService {

    public synchronized void childService() {
        try {
            while (i > 0) {
                // 子类和父类同时操作父类中的变量i,此时子类已经持有了子类的对象锁
                i--;
                System.out.println("子类操作数:i=" + i);
                Thread.sleep(100);
                // 调用父类方法,但此时依然只是持有子类对象的锁(第二次获取锁)
                this.service();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Create a thread of execution

public class MyThread extends Thread {


    @Override
    public void run() {
        Child_Service child_service = new Child_Service();
        child_service.childService();
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

Print results

Seen from the above results, the parent and child classes are alternately printed, indicating that the lock is reentrant, if the lock does not re-entrant, then the parent class method would not executed, that is, holding a lock case, the lock can be acquired again, this transition is locked reentrancy.

Popular terms: When a thread requests an object held by another thread lock, the thread is blocked, and when the thread requests an object held by their own lock if the lock is reentrant lock, the request will succeed, otherwise obstruction.

In the same thread that calls itself the class methods other synchronized / synchronized block or method calls the parent class / block will not impede the implementation of the thread. That same thread on the same object lock is reentrant, but the same thread can acquire the same lock many times, that can be repeatedly re-entry.

Reentrant locks for reentrant mechanism or principle is: each associated with a thread-locking holders and counter when the counter is 0 indicates that the lock is not held by any thread, any thread may acquire the lock and call corresponding method; a thread when the request is successful, the JVM will remember the thread holding the lock, and the counter is set to 1; another thread requests the lock case, you must wait; and if the thread holding the lock again this lock request, the lock can get again, while the counter is incremented; when a thread exits sync block, the counter is decremented, if the counter is 0, the lock is released.

(2) the synchronization does not have inheritance

When subclass overrides parent Synchronized methods, if not before again Synchronized modified method that does not have synchronization.

4, Synchronized sync block of statements, any object monitor

(1) the benefits of using the synchronization code block is, the only need to synchronize the synchronization code, to improve process efficiency.

(2) Synchronized (this) code block lock, the lock is the current object, the object is locked. This is because the use of this as the "object monitor", then this specifies the current object.

(. 3) Synchronized (non this) The advantage of using, if there is no synchronous relationship between a number of Synchronized method, and methods in a class method, then you can use the Synchronized (non this), so that each method has its own lock, thereby increasing process efficiency.

Synchronized (non this) so that the lock more refined control, synchronized (this) lock the entire object is then synchronized (non this) can be refined to specific methods inside.

java can be used in "any object" as "Monitor objects" to achieve synchronization. This "either variable" Most of instance variables or method parameters.

Code Example:

Create MyService class, a method wherein an object monitor (this), another method of this non-monitor

public class MyService {

    private String anyString = new String();

    public void a(){
        try {
            // 此锁非this对象锁
            synchronized (anyString){
                System.out.println(Thread.currentThread().getName()+":进入方法A...");
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName()+":方法A结束...");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public void b(){
        // this对象锁
        synchronized (this) {
            System.out.println(Thread.currentThread().getName()+":方法B开始...");
            System.out.println(Thread.currentThread().getName()+":方法B结束...");
        }
    }
}

Create a thread - the thread A:

public class ThreadA extends Thread {

    private MyService myService;

    public ThreadA(MyService myService){
        super();
        this.myService = myService;
    }

    public void run(){
        myService.a();
    }

    public static void main(String[] args) {
        // 代码执行如下
        MyService myService = new MyService();
        ThreadA threadA = new ThreadA(myService);
        ThreadB threadB = new ThreadB(myService);
        threadA.start();
        threadB.start();
    }
}

Create a thread - Thread B:

public class ThreadB extends Thread {

    private MyService myService;

    public ThreadB(MyService myService){
        super();
        this.myService = myService;
    }

    public void run(){
        myService.b();
    }
}

Results of the:

Due to the different object monitor, the result is the asynchronous operation.

4.1 Data type String constant pool characteristics

String constant pool has a cache function in the JVM , so the use of String objects as a monitor, you need to pay attention to this issue

For constant pool characteristics? See this article:

https://www.cnblogs.com/syp172654682/p/8082625.html

Test code:

Creating MyService class

public class MyService {

    public void method(String str){
        try {
            synchronized (str) {
                System.out.println(Thread.currentThread().getName()+":静态方法A开始...");
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName()+":静态方法A结束...");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

--A threads to create a thread

public class ThreadA extends Thread {

    private MyService myService;

    public ThreadA(MyService myService){
        super();
        this.myService = myService;
    }

    public void run(){
        myService.method("str");
    }

    public static void main(String[] args) {
        MyService myService = new MyService();
        ThreadA threadA = new ThreadA(myService);
        ThreadB threadB = new ThreadB(myService);
        threadA.start();
        threadB.start();
    }
}

--B threads to create a thread

public class ThreadB extends Thread {

    private MyService myService;

    public ThreadB(MyService myService){
        super();
        this.myService = myService;
    }

    public void run(){
        myService.method("str");
    }
}

Results of the:

These results suggest that the two threads that hold the same lock, monitor their objects are "str", for the same monitor.

Why does this happen? Obviously twice pass in, let's look a little test

public static void main(String[] args) {
        String strA = "string";
        String strB = "string";
        String strC = new String("string");
        String strD = new String("string");
        System.out.println("字符串A、B相等吗?"+(strA == strB));
        System.out.println("字符串C、D相等吗?"+(strC == strD));
    }

Output:

说明在常量池中创建的对象是同一个对象,而使用new关键字创建的变量是重新分配内存的,是两个不同的对象。

5、静态同步Synchronized方法和Synchronized(class)代码块,类锁

关键字Synchronized应用在静态方法上,对Class类进行持锁,类锁和对象锁是两种不同的锁。

测试代码:

创建MyService类:

public class MyService {

    /**
     * 静态方法锁:class锁
     */
    public synchronized static void a(){
        try {
            System.out.println(Thread.currentThread().getName()+":静态方法A开始...");
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName()+":静态方法A结束...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 静态方法锁:class锁
     */
    public synchronized static void b(){
        System.out.println(Thread.currentThread().getName()+":静态方法B开始...");
        System.out.println(Thread.currentThread().getName()+":静态方法B结束...");
    }

    /**
     * 非静态方法锁:对象锁
     */
    public synchronized void c(){
        System.out.println(Thread.currentThread().getName()+":方法C开始...");
        System.out.println(Thread.currentThread().getName()+":方法C结束...");
    }
}

创建线程——A线程:

public class ThreadA extends Thread {

    private MyService myService;

    public ThreadA(MyService myService){
        super();
        this.myService = myService;
    }

    public void run(){
        myService.a();
    }

    public static void main(String[] args) {
        // 执行代码
        MyService myService = new MyService();
        ThreadA threadA = new ThreadA(myService);
        ThreadB threadB = new ThreadB(myService);
        ThreadC threadC = new ThreadC(myService);
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

创建线程——B线程:

public class ThreadB extends Thread {

    private MyService myService;

    public ThreadB(MyService myService){
        super();
        this.myService = myService;
    }

    public void run(){
        myService.b();
    }
}

创建线程——C线程:

public class ThreadC extends Thread {

    private MyService myService;

    public ThreadC(MyService myService){
        super();
        this.myService = myService;
    }

    public void run(){
        myService.c();
    }
}

执行结果:

从执行结果可以看出,持相同类锁的两个静态方法是同步执行的,而持对象锁的方法则与静态方法异步执行,因此,类锁和对象锁并不是同一把锁。

那么这两把锁到底有什么区别呢?

5.1静态Synchronized方法和非静态Synchronized方法的区别

区别:

静态Synchronized方法,多个类对象持一把锁

非静态Synchronized方法,多个对象多把锁

同样还是用上边的MyService类作为测试:

A线程:

public class ThreadA extends Thread {

    private MyService myService;

    public ThreadA(MyService myService){
        super();
        this.myService = myService;
    }

    public void run(){
        myService.a();
    }

    public static void main(String[] args) {
        // 多个MySevice对象,线程A,B同时执行方法a
        MyService myService1 = new MyService();
        MyService myService2 = new MyService();
        ThreadA threadA = new ThreadA(myService1);
        ThreadB threadB = new ThreadB(myService2);
        threadA.start();
        threadB.start();
    }
}

B线程:

public class ThreadB extends Thread {

    private MyService myService;

    public ThreadB(MyService myService){
        super();
        this.myService = myService;
    }

    public void run(){
        myService.a();
    }
}

执行结果:

静态的Synchronized方法,即使在多个对象的情况下,它也是同步执行的,它并不具备多对象多把锁的特征。

6、Synchronized关键字和内部类及静态内部类

关于内部类:

1.内部类中隐含有外部类的引用。所以可以使用外部类中所有的东西。

2.创建内部类对象时,必须与外围类对象相关联,创建外围类对象之前,不可能创建内部类的对象。

特殊情况:静态内部类(也称嵌套类)

1.创建静态内部类是不需要外部类。

2.静态内部类不能访问外部类的非静态成员。

明白以上内部类的知识后,其实对内部类多线程锁的使用也就基本上明白了,内部类可以看成是放在一个类里边的普通类,因此锁的使用,也就跟普通的类是一样的,关键是要区分锁到底使用的是什么样的锁。

然后静态的内部类只是区别这个内部类跟外部类的关系,在使用锁时,性质与普通内一样。

一般,内部类中有两个同步方法,但使用的却是不同锁,那么执行是异步的。

测试代码:

public class MyService {

    static class Child01{
        // 使用Child02对象锁
        public void method01(Child02 child02){
            synchronized (child02) {
                try {
                    System.out.println(Thread.currentThread().getName()+":进入child01的method01方法...");
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName()+":退出child01的method01方法...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        // 持有child01的对象锁
        public synchronized void method02(){
            try {
                System.out.println(Thread.currentThread().getName()+":进入child01的method02方法...");
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName()+":退出child01的method02方法...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    static class Child02{
        // 使用Child02对象锁
        public synchronized void method01(){
            try {
                System.out.println(Thread.currentThread().getName()+":进入child02的method01方法...");
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName()+":退出child02的method01方法...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Child01 child01 = new Child01();
        Child02 child02 = new Child02();
        // 线程1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 传入child02作为锁对象
                child01.method01(child02);
            }
        });
        // 线程2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 使用child01对象作为锁
                child01.method02();
            }
        });
        // 线程3
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 使用child02对象作为锁
                child02.method01();
            }
        });
        t1.start();
        t2.start();
        t3.start();
    }
}

执行结果:

分析上述执行结果:

线程1和线程0是异步执行的,这是因为两个方法所持有的锁不同,线程1持有的是Child02的锁,线程0持有的是Child01的锁,锁不一样,所以执行异步。

线程2和线程0是同步执行的,它们共同争夺Child02的锁,所以线程2必须等线程1执行完才能继续执行,在线程0执行时,线程2成阻塞状态。

总之,不管是普通内还是内部类,关键看的还是持有的锁对象是否相同,相同则同步执行,不同则异步执行。

Guess you like

Origin blog.csdn.net/swadian2008/article/details/99328700