zoukankan      html  css  js  c++  java
  • java synchronized关键字浅析

    synchronized这个关键字想必学Java的人都应该知道。

    直接上例子:

    方法级别实例

    public class AtomicInteger {
    
        private int index;
    
        public synchronized int addAndGet() {
            try {
                Thread.sleep(2000l);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return ++ index;
        }
    
        public int get() {
            return index;
        }
    
    }

    一个自动增长的类,不解释了。

    看测试代码:

    final AtomicInteger ai = new AtomicInteger();
    
    new Thread(new Runnable() {
        @Override
        public void run() {
            ai.addAndGet();
            System.out.println("thread1 finish sleep");
        }
    }).start();
    
    new Thread(new Runnable() {
        @Override
        public void run() {
            ai.addAndGet();
            System.out.println("thread2 finish sleep");
        }
    }).start();

    这里例子执行结果是这样的:

    过2秒,输出thread1 finish sleep

    然后再过2秒,输出thread2 finish sleep。

    现在我们把AtomicInteger类里的addAndGet方法前的synchronized关键字去掉。

    执行结果是这样的:

    过2秒,输出  thread1 finish sleep  然后马上输出  thread2 finish sleep (可能先输出thread2 finish sleep)

    分析

    java中synchronized就是"加锁"的意思, 一个实例中的方法如果加上了synchronized,那么这个方法如果被调用了,就会自动加上一个锁,如果其他线程想要再次调用这个实例的这个方法,那么需要等待之前的线程执行完成之后才能执行。

    因此,之前的实例方法之前有synchronized的执行结果就是过2秒输出一段,再过2秒再次输出。

    类级别实例

     下面。 我们给AtomicInteger加一个静态同步方法。

    public static synchronized void sync() {
        System.out.println("now synchronized. wait 4 seconds.");
        try {
            Thread.sleep(2000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    测试代码:

    final AtomicInteger ai = new AtomicInteger();
    
    AtomicInteger.sync();
    
    new Thread(new Runnable() {
        @Override
        public void run() {
            ai.addAndGet();
            System.out.println("thread1 finish sleep");
        }
    }).start();
    
    new Thread(new Runnable() {
        @Override
        public void run() {
            ai.addAndGet();
            System.out.println("thread2 finish sleep");
        }
    }).start();

    这个测试结果执行结果是这样的:

    先输出  now synchronized. wait 4 seconds.

    过4秒(因为静态同步方法需要2秒,addAndGet方法也需要2秒),输出 thread1 finish sleep  

    再过2秒,输出 thread2 finish sleep

    分析

    从上述类级别的实例可以看出  ->   静态同步方法作用的范围是类,执行了静态同步方法,静态同步方法执行的时候,这个类中的所有方法(无论是否有synchronized, 无论是否静态)都必须等待这个静态同步方法执行完成之后才能进行

    如果我们把AtomicInteger的addAndGet方法的synchronized关键字去掉,那么执行结果是这样的。

    先输出  now synchronized. wait 4 seconds.

    过4秒,输出 thread1 finish sleep  

    然后马上又输出 thread2 finish sleep

    再来看个例子:

    final AtomicInteger ai1 = new AtomicInteger();
    final AtomicInteger ai2 = new AtomicInteger();
    
    AtomicInteger.sync();
    
    new Thread(new Runnable() {
        @Override
        public void run() {
            ai1.addAndGet();
            System.out.println("ai1 finish sleep");
        }
    }).start();
    
    new Thread(new Runnable() {
        @Override
        public void run() {
            ai2.addAndGet();
            System.out.println("ai2 finish sleep");
        }
    }).start();

    执行结果是这样的。

    先输出  now synchronized. wait 4 seconds.

    过4秒,输出 ai1 finish sleep  

    然后马上又输出 ai2 finish sleep

    小结: 由于静态同步方法是作用于类级别的,因此实例级别的方法调用都收到影响。所以执行AtomicInteger.sync();的时候ai1,ai2都在等待。 当执行完毕的时候,ai1和ai2由于是不同实例,方法级别的synchronized当然互不影响。

    资源级别的实例

    资源级别的同步其实跟方法级别的同步是一样的,只不过资源级别的同步的作用更加细节一下,它作用的对象是基于资源的。比如实例中的成员变量。

    死锁,可以完美说明资源级别的同步。

    public class DeadLock {
    
        private static Object obj1 = new Object();
        private static Object obj2 = new Object();
    
        public static void main(String[] args) {
            new Thread(new SubThread1()).start();
            new Thread(new SubThread2()).start();
        }
    
        private static class SubThread1 implements Runnable {
            @Override
            public void run() {
                synchronized (obj1) {
                    try {
                        Thread.sleep(1000l);
                        System.out.println("thread1 synchronized obj1 and wait for obj2.");
                        synchronized (obj2) {
                            System.out.println("thread1 synchronized obj2");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        private static class SubThread2 implements Runnable {
            @Override
            public void run() {
                synchronized (obj2) {
                    try {
                        Thread.sleep(1000l);
                        System.out.println("thread2 synchronized obj2 and wait for obj1.");
                        synchronized (obj1) {
                            System.out.println("thread2 synchronized obj1");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }

    从死锁这个实例可以看出。

    子线程1同步obj1资源的时候,其他线程是不能获得obj1这个资源的。同理,子线程同步obj2资源的时候,其他线程是不能获得obj2这个资源的。

    因此,子线程1在同步obj1之后,会等待obj2的释放(obj2已经被子线程2同步了,子线程1不能获得);而子线程2在同步obj2之后,会等待obj1的释放(obj1已经被子线程1同步了,子线程2不能获得)。

    所以,这个程序将永远地等待下去。 这也就是著名的死锁问题。

    我们可以通过jdk提供的一些命令查看是否死锁。

    首先通过jps查看当前jdk启动的进程。

    找到死锁这个程序的进程号6552之后再通过  jstack -l 6552 查看堆栈信息。

    看到jstack发现了一个死锁。

    总结

    synchronized关键字可以作用在多个场景之下,我们需要区分清楚各个场景的区别,以便我们设计出符合高并发的程序。

    之前对java并发这块了解的太少了.. 面试的时候也问到这些问题,发现自己根本答不上来。

  • 相关阅读:
    简单构建一个xmlhttp对象池合理创建和使用xmlhttp对象
    iBATIS.net获取运行时sql语句
    不做自了汉,大家好才是真的好
    sql查询,nolock写还是不写,这是一个问题
    Sublime Text 2 快捷键用法大全(转)
    javascript设计模式入门之策略模式
    记一次外单前端页面编写小结
    代码驾驭
    一次项目总结,内容设置页面
    【百度地图API】今日小年大进步,齐头共进贺佳节——API优化升级上线,不再增加内存消耗
  • 原文地址:https://www.cnblogs.com/fangjian0423/p/3614637.html
Copyright © 2011-2022 走看看