Java线程安全synchronize学习

Java中,synchronized关键字有2种用法:
  • 作为关键字修饰方法
  • 修饰一个代码块
  • [TOC]线程争用为了探究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:

    相关内容推荐