美文网首页
Java中同一个类中不同的synchronized方法是否可以并

Java中同一个类中不同的synchronized方法是否可以并

作者: 元亨利贞o | 来源:发表于2016-04-29 23:28 被阅读3903次

    更通俗简单的问法: "Java中两个线程是否可以同是否问一个对象的两个不同的synchronized方法????"

    答案是: 不可以!!!

    也许这个问题在你看来显而易见, 但是我却一直以为:

    ~~几个不同线程同时访问某个类的同一个synchronized方法时, 一个线程执行时, 其他线程都等待; 若是几个不同线程访问的是一个类不同的synchronized, 则可以同时执行. ~~

    让大神们见笑了~~

    ======
    但是如果你也有和我一样的观念, 那么就请仔细阅读下面的文字!!

    多个线程访问同一个类的synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了类java的内置锁, 即锁住的是方法所属对象本身. 同一个锁某个时刻只能被一个执行线程所获取, 因此其他线程都得等待锁的释放. 因此就算你有多余的cpu可以执行, 但是你没有锁, 所以你还是不能进入synchronized方法执行, CPU因此而空闲. 如果某个线程长期持有一个竞争激烈的锁, 那么将导致其他线程都因等待所的释放而被挂起, 从而导致CPU无法得到利用, 系统吞吐量低下. 因此要尽量避免某个线程对锁的长期占有 !

    下面给出验证代码, 君自斟酌:

    public class SyncMethod {
    
        public synchronized void syncMethod2() {
            try {
                System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已经获取内置锁`SyncMethod.this`)");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即将释放内置锁`SyncMethod.this`)");
        }
    
        public synchronized void syncMethod1() {
            System.out.println("######################## (syncMethod1, 已经获取内置锁`SyncMethod.this`, 并即将退出)");
        }
    
    
        static class Thread1 extends Thread {
            SyncMethod syncMethod;
    
            public Thread1(SyncMethod syncMethod) {
                this.syncMethod = syncMethod;
            }
    
            @Override
            public void run() {
                syncMethod.syncMethod2();
            }
        }
    
        static class Thread2 extends Thread {
            SyncMethod syncMethod;
    
            public Thread2(SyncMethod syncMethod) {
                this.syncMethod = syncMethod;
            }
    
            @Override
            public void run() {
                System.out.println("Thread2 running ...");
                syncMethod.syncMethod1();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            SyncMethod syncMethod = new SyncMethod();
            Thread1 thread1 = new Thread1(syncMethod);
            Thread2 thread2 = new Thread2(syncMethod);
    
            thread1.start();    //先执行, 以便抢占锁
            Thread.sleep(500); //放弃cpu, 让thread1执行, 以便获的锁
    
            thread2.start(); //在syncMethod1()方法获得锁时, 看看syncMethod2()方法能否执行
    
            /*
            能否并发执行同一个对象不同的synchronized方法, 即看看能否同时进入一个对象synchronized方法块
    
            1. 创建一个有两个synchronized方法的对象`syncMethod`
            2. 先启动一个线程(Thread1), 并让其进入syncMethod对象的sychronized方法(syncMethod1)内, 并使其停在synchronized方法内
            3. 再启动一个线程(Thread2), 并执行syncMethod对象的一个synchronized方法(syncMethod2), 看看能否进入此方法内
    
            输出如下: 
            @@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已经获取内置锁`SyncMethod.this`)
            Thread2 running ...
            @@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即将释放内置锁`SyncMethod.this`)
            ######################## (syncMethod1, 已经获取内置锁`SyncMethod.this`, 并即将退出)
    
            结果分析: 
                观察显示, 在输出`Thread2 running ...`后会暂停数秒(Thread2无法获得所而被挂起, 因为锁已经被Thread1持有).
                
                如果不同线程可以同时访问同一个对象不同synchronized方法的话, 
                在有足够cpu时间片时(Thread1在调用syncMethod1时使用Thread.sleep放弃了cpu), 
                Thread2调用的syncMethod2方法应该马上执行, 也就是syncMethod2方法中的语句在`Thread2 running ...`语句输出后马上输出, 
                而不是等待数秒才输出 (应为此时没有其他线程跟Thread2竞争cpu, 况且现在的电脑都不只一个cpu), 
                因此得出结论: "不同线程不能同时执行一个对象的不同synchronized方法"
            
                其实此结论是显而易见的, 原理前面已经阐明, 此处不再赘述.
            */
    
        }
    }
    

    下面是一些关于使用锁的一些建议:
    为了避免对锁的竞争, 你可以使用锁分解,锁分段以及减少线程持有锁的时间, 如果上诉程序中的syncMethod1和syncMethod2方法是两个不相干的方法(请求的资源不存在关系), 那么这两个方法可以分别使用两个不同的锁, 改造后的SyncMethod类如下:

    public class SyncMethod {
        private Object lock1 = new Object();
        private Object lock2 = new Object();
        
        public void syncMethod2() {
            synchronized (lock1) {
                try {
                    System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已经获取内置锁`SyncMethod.this`)");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即将释放内置锁`SyncMethod.this`)");
            }
        }
    
        public void syncMethod1() {
            synchronized (lock2) {
                System.out.println("######################## (syncMethod1, 已经获取内置锁`SyncMethod.this`, 并即将退出)");
            }
        }
    }
    

    改造之后Thread1和Thread2就不会发生, 由于锁竞争而挂起的情况了.

    当然, 如果syncMethod1中耗时操作与锁定的资源无关, 那么也可以将耗时操作移出同步块. 在上述改造的基础上对syncMethod1的进一步改造如下:

    public void syncMethod2() {
        synchronized (lock1) {
            System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 已经获取内置锁`SyncMethod.this`)");
            System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@ (syncMethod2, 即将释放内置锁`SyncMethod.this`)");
        }
    
    
        //将耗时操作移出同步块
        try {
            Thread.sleep(5000);//与同步使用的资源无关的耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    

    .
    关于并发编程, 圣经在此 => 《Java并发编程实战》
    有些观念生而有之, 久而久之,我们就认为它们是对的合理的,然而并非如此。如果某一天你对某个曾经深信不疑的观念有了疑问, 那么赶紧行动起来,请用你的理性思考它,验证它。

    共勉!

    相关文章

      网友评论

          本文标题:Java中同一个类中不同的synchronized方法是否可以并

          本文链接:https://www.haomeiwen.com/subject/qvhkrttx.html