如何理解synchronized | Java Debug 笔记

425 阅读4分钟

如何理解synchronized | Java Debug 笔记

本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看<活动链接>

提问

如何理解synchronized关键字?

具体地说,我对synchronized关键字的用法和重要性有如下疑问:

synchronized关键字的意义是什么? 什么时候方法前面应该使用synchronizedsynchronized从程序上和逻辑上分别意味着什么?

最热门回答

回答1:

synchronized关键字是在不同线程需要读取和写入相同变量、对象和资源时使用的。这是Java中的重要话题,以下是Sun的引文:

使用了synchronized关键字的同步方法提供了一种防止线程干扰和内存一致性错误的简单策略:如果一个对象对一个以上线程可见,则对该对象变量的所有读取或写入都将通过该同步方法完成。

简而言之:当有两个线程正在读取和写入同一个“资源”时,比如说一个名为foo的变量,您需要确保这些线程以原子方式访问该变量。如果没有synchronized关键字,您的线程1可能看不到线程2对foo所做的更改,或者更糟的是,它可能只更改了一半。这在逻辑上不会是您所期望的。

同样,这是Java中非常重要的话题。要了解更多信息,请在SO和Interwebs上探索有关以下主题:

继续探索这些主题,直到大师Brian Goetz的名字与大脑中的“并发”一词永久关联。

(译者注:Brian Goetz 是Oracle 的Java 语言架构师,也是JSR-335(Lambda Expressions for the Java Language)规范的负责人。 他畅销书《 Java 并发编程实战》的作者,并且经常出席各大业内会议。

回答2:

好吧,我认为我们已经有了足够的理论解释,所以请看一下这段代码:

public class SOP {
    public static void print(String s) {
        System.out.println(s+"\n");
    }
}

public class TestThread extends Thread {
    String name;
    TheDemo theDemo;
    public TestThread(String name,TheDemo theDemo) {
        this.theDemo = theDemo;
        this.name = name;
        start();
    }
    @Override
    public void run() {
        theDemo.test(name);
    }
}

public class TheDemo {
    public synchronized void test(String name) {
        for(int i=0;i<10;i++) {
            SOP.print(name + " :: "+i);
            try{
                Thread.sleep(500);
            } catch (Exception e) {
                SOP.print(e.getMessage());
            }
        }
    }
    public static void main(String[] args) {
        TheDemo theDemo = new TheDemo();
        new TestThread("THREAD 1",theDemo);
        new TestThread("THREAD 2",theDemo);
        new TestThread("THREAD 3",theDemo);
    }
}

注意:只要前一个线程的执行未完成,synchronized就会阻止下一个线程对方法test()的调用。线程一次可以访问此方法。如果test方法没有添加synchronized关键字,则所有线程都可以同时访问此方法。

当线程调用对象的同步方法 test()(此处对象是TheDemo类的实例)时,它获取该对象的锁,任何新线程都不能调用同一对象的任何同步方法,只要前一个线程已获得锁的不会释放锁。

当调用该类的任何静态同步方法时,也会发生类似的情况。线程获取与该类相关联的锁(在这种情况下,任何类都可以调用该类实例的任何非静态同步方法,因为该对象级锁仍然可用)。只要当前持有该锁的线程未释放该类级别的锁,任何其他线程都将无法调用该类的任何静态同步方法。

输出:

THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 1 :: 4
THREAD 1 :: 5
THREAD 1 :: 6
THREAD 1 :: 7
THREAD 1 :: 8
THREAD 1 :: 9
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 3 :: 4
THREAD 3 :: 5
THREAD 3 :: 6
THREAD 3 :: 7
THREAD 3 :: 8
THREAD 3 :: 9
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 2 :: 4
THREAD 2 :: 5
THREAD 2 :: 6
THREAD 2 :: 7
THREAD 2 :: 8
THREAD 2 :: 9

test()方法去掉synchronized关键字后的输出:

THREAD 1 :: 0
THREAD 2 :: 0
THREAD 3 :: 0
THREAD 1 :: 1
THREAD 2 :: 1
THREAD 3 :: 1
THREAD 1 :: 2
THREAD 2 :: 2
THREAD 3 :: 2
THREAD 1 :: 3
THREAD 2 :: 3
THREAD 3 :: 3
THREAD 1 :: 4
THREAD 2 :: 4
THREAD 3 :: 4
THREAD 1 :: 5
THREAD 2 :: 5
THREAD 3 :: 5
THREAD 1 :: 6
THREAD 2 :: 6
THREAD 3 :: 6
THREAD 1 :: 7
THREAD 2 :: 7
THREAD 3 :: 7
THREAD 1 :: 8
THREAD 2 :: 8
THREAD 3 :: 8
THREAD 1 :: 9
THREAD 2 :: 9
THREAD 3 :: 9

回答3:

synchronized关键字可防止多个线程同时访问代码或对象块。 Hashtable的所有方法都是同步的,因此一次只能有一个线程执行它们中的任何一个。

当使用非同步结构(例如HashMap)时,必须在代码中构建线程安全功能,以防止出现一致性错误。

原文链接