Java线程安全synchronize学习

Java中,synchronized关键字有2种用法:

  1. 作为关键字修饰方法
  2. 修饰一个代码块

线程争用

为了探究synchronized的具体用法,可以用一个简单的程序来说明:

package fc.learn.java.synchronize;

import java.util.Random;

public class LearningSynchronized {

    public enum SyncTypeTag {
        OBJ_METHOD, CLASS_METHOD, KEYWARD_METHOD, NORMAL_METHOD
    }

    static class MyRunnable implements Runnable{

        private void counting(){
            for (int i = 0; i< 5; i++){
                System.out.println(String.format("Thread[%s] count for [%s]", Thread.currentThread().getName(), i));
                int sleepSeconds = new Random().nextInt();
                sleepSeconds = sleepSeconds % 10;
                sleepSeconds = sleepSeconds * sleepSeconds;
                sleepSeconds = sleepSeconds % 5;
                System.out.println(String.format("Thread[%s] sleep for 0.%s seconds", Thread.currentThread().getName(), sleepSeconds));
                try {
                    Thread.sleep(sleepSeconds * 100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        private void synchronizeObjectMethod() {
            synchronized (this){
                counting();
            }
        }

        private void synchronizeClasstMethod(){
            synchronized (MyRunnable.class){
                counting();
            }
        }

        private synchronized void synchronizedMethod(){
            counting();
        }


        private void normalMethod(){
            counting();
        }

        private SyncTypeTag synType;
        public MyRunnable(SyncTypeTag type){
            this.synType = type;
        }

        @Override
        public void run() {
            if (this.synType == SyncTypeTag.OBJ_METHOD) {
                synchronizeObjectMethod();
            } else if (this.synType == SyncTypeTag.CLASS_METHOD) {
                synchronizeClasstMethod();
            } else if (this.synType == SyncTypeTag.KEYWARD_METHOD) {
                synchronizedMethod();
            } else if (this.synType == SyncTypeTag.NORMAL_METHOD) {
                normalMethod();
            }
        }
    }

    public static void main(String[] args) {
        Runnable r1 = new MyRunnable(SyncTypeTag.NORMAL_METHOD);
        Runnable r2 = new MyRunnable(SyncTypeTag.NORMAL_METHOD);

        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        t2.start();
    }

}

运行代码,可以获得结果如下:

Thread[Thread-1] count for [0]
Thread[Thread-0] count for [0]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [3]
Thread[Thread-0] count for [2]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.1 seconds

也就是说,普通情况下,counting()方法会被线程争用

类锁 synchronized(class)

那么我们加锁试试呢?首先试试对class加锁:

// 之前代码太多不再赘述,此处只展示main方法
public static void main(String[] args) {
    Runnable r1 = new MyRunnable(SyncTypeTag.CLASS_METHOD);
    Runnable r2 = new MyRunnable(SyncTypeTag.CLASS_METHOD);

    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);

    t1.start();
    t2.start();
}

可以得到如下结果:

Thread[Thread-0] count for [0]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-0] count for [2]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [0]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.1 seconds

此时,线程不再争用,目的达到。

对象锁 synchronized(object)

那么试试对象锁:

// 仔细观察,这里用的 this 也就是说,每个线程用的不是同一把锁
private void synchronizeObjectMethod() {
    synchronized (this){
        counting();
    }
}

public static void main(String[] args) {
    Runnable r1 = new MyRunnable(SyncTypeTag.CLASS_METHOD);
    Runnable r2 = new MyRunnable(SyncTypeTag.CLASS_METHOD);

    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);

    t1.start();
    t2.start();
}

运行代码:

Thread[Thread-1] count for [0]
Thread[Thread-0] count for [0]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [2]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.0 seconds

可以发现,锁并不起作用。这个原因也很容易理解,因为synchronizeObjectMethod方法中用的synchronized(this)进行加锁,而我们有2个进程对象在对counting()方法进行操作,所以会发生争用。如果代码修改为这样:

// synchronizedObjectMethod修改成这样:
private void synchronizeObjectMethod(Object globalLock) {
    synchronized (globalLock){
        counting();
    }
}
// 构造方法和成员变量:
private SyncTypeTag synType;
private Object globalLock;
public MyRunnable(SyncTypeTag type, Object lock){
    this.synType = type;
    this.globalLock = lock;
}

@Override
public void run() {
    if (this.synType == SyncTypeTag.OBJ_METHOD) {
        synchronizeObjectMethod(this.globalLock);
    } else if (this.synType == SyncTypeTag.CLASS_METHOD) {
        synchronizeClasstMethod();
    } else if (this.synType == SyncTypeTag.KEYWARD_METHOD) {
        synchronizedMethod();
    } else if (this.synType == SyncTypeTag.NORMAL_METHOD) {
        normalMethod();
    }
}

// main 方法:
public static void main(String[] args) {
    Object globalLock = new Object();
    Runnable r1 = new MyRunnable(SyncTypeTag.OBJ_METHOD, globalLock);
    Runnable r2 = new MyRunnable(SyncTypeTag.OBJ_METHOD, globalLock);

    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);

    t1.start();
    t2.start();
}

运行代码:

Thread[Thread-0] count for [0]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [2]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-1] count for [0]
Thread[Thread-1] sleep for 0.0 seconds
Thread[Thread-1] count for [1]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.4 seconds

争用消失。

关键字 synchronized method()

接下来再试试synchronized关键字直接修饰方法:

public static void main(String[] args) {
    Object globalLock = new Object();
    Runnable r1 = new MyRunnable(SyncTypeTag.KEYWARD_METHOD, globalLock);
    Runnable r2 = new MyRunnable(SyncTypeTag.KEYWARD_METHOD, globalLock);

    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);

    t1.start();
    t2.start();
}

运行代码:

Thread[Thread-0] count for [0]
Thread[Thread-1] count for [0]
Thread[Thread-0] sleep for 0.0 seconds
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-0] count for [1]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-1] count for [1]
Thread[Thread-0] count for [2]
Thread[Thread-1] sleep for 0.1 seconds
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-0] count for [3]
Thread[Thread-0] sleep for 0.1 seconds
Thread[Thread-1] count for [2]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-0] count for [4]
Thread[Thread-0] sleep for 0.4 seconds
Thread[Thread-1] count for [3]
Thread[Thread-1] sleep for 0.4 seconds
Thread[Thread-1] count for [4]
Thread[Thread-1] sleep for 0.1 seconds

依旧会起争用,说明synchronized修饰方法,等于 synchronized(this)

References:
http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html

原文地址:https://www.cnblogs.com/tflowingcloud/p/8428743.html