我有一些关于同步关键字的用法和意义的问题。

synchronized关键字有什么意义? 什么时候应该同步方法? 它在程序上和逻辑上意味着什么?


当前回答

synchronized关键字是关于不同的线程读写相同的变量、对象和资源。这在Java中不是一个微不足道的话题,但这里引用Sun的一段话:

同步方法可以实现简单的 防止线程的策略 干扰和记忆一致性 错误:如果对象是可见的 多于一个线程,全部读取或 写入该对象的变量为 通过同步方法完成。

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

同样,这在Java中是一个重要的主题。要了解更多,请在这里探索关于SO和互联网的主题:

并发性 Java内存模型

继续探索这些话题,直到“Brian Goetz”这个名字在你的脑海中与“并发性”这个术语永久地联系在一起。

其他回答

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

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

Synchronized的意思是,如果在特定对象上使用Synchronized块,那么与单个对象关联的多个线程可以防止脏读写。为了让你更清楚,让我们举个例子:

class MyRunnable implements Runnable {
    int var = 10;
    @Override
    public void run() {
        call();
    }

    public void call() {
        synchronized (this) {
            for (int i = 0; i < 4; i++) {
                var++;
                System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
            }
        }
    }
}

public class MutlipleThreadsRunnable {
    public static void main(String[] args) {
        MyRunnable runnable1 = new MyRunnable();
        MyRunnable runnable2 = new MyRunnable();
        Thread t1 = new Thread(runnable1);
        t1.setName("Thread -1");
        Thread t2 = new Thread(runnable2);
        t2.setName("Thread -2");
        Thread t3 = new Thread(runnable1);
        t3.setName("Thread -3");
        t1.start();
        t2.start();
        t3.start();
    }
}

我们已经创建了两个MyRunnable类对象,runnable1与线程1和线程3共享,而runnable2仅与线程2共享。 现在,当t1和t3启动时没有使用synchronized, PFB输出表明线程1和线程3同时影响var值,而对于线程2,var有自己的内存。

Without Synchronized keyword

    Current Thread Thread -1 var value 11
    Current Thread Thread -2 var value 11
    Current Thread Thread -2 var value 12
    Current Thread Thread -2 var value 13
    Current Thread Thread -2 var value 14
    Current Thread Thread -1 var value 12
    Current Thread Thread -3 var value 13
    Current Thread Thread -3 var value 15
    Current Thread Thread -1 var value 14
    Current Thread Thread -1 var value 17
    Current Thread Thread -3 var value 16
    Current Thread Thread -3 var value 18

使用Synchronzied,线程3在所有场景中等待线程1完成。有两个锁,一个在runnable1上,由线程1和线程3共享,另一个在runnable2上,仅由线程2共享。

Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18

好了,我认为我们已经有了足够多的理论解释,所以考虑一下这段代码

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'时(这里的对象是'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

输出未同步

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

概述

Java中的Synchronized关键字与线程安全有关,也就是说,当多个线程读或写同一个变量时。 这可以直接发生(通过访问相同的变量)或间接发生(通过使用使用访问相同变量的另一个类的类)。

synchronized关键字用于定义一个代码块,其中多个线程可以以安全的方式访问同一个变量。

更深层次的

从语法上看,synchronized关键字接受一个Object作为它的参数(称为锁对象),然后后面跟着一个{block of code}。

When execution encounters this keyword, the current thread tries to "lock/acquire/own" (take your pick) the lock object and execute the associated block of code after the lock has been acquired. Any writes to variables inside the synchronized code block are guaranteed to be visible to every other thread that similarly executes code inside a synchronized code block using the same lock object. Only one thread at a time can hold the lock, during which time all other threads trying to acquire the same lock object will wait (pause their execution). The lock will be released when execution exits the synchronized code block.

同步的方法:

将synchronized关键字添加到方法定义中等于将整个方法体包装在同步代码块中,锁对象为this(对于实例方法)和ClassInQuestion.getClass()(对于类方法)。

—实例方法是没有静态关键字的方法。 —类方法是具有静态关键字的方法。

技术

如果没有同步,就无法保证以何种顺序进行读写,可能会给变量留下垃圾。 (例如,一个变量可能由一个线程写入一半的比特,另一个线程写入一半的比特,使变量处于两个线程都没有尝试写入的状态,而是两者结合在一起的混乱状态。)

在另一个线程读取之前(时钟时间)完成一个线程的写操作是不够的,因为硬件可能已经缓存了变量的值,读取线程将看到缓存的值,而不是写入它的值。

结论

因此,在Java的情况下,您必须遵循Java内存模型,以确保线程错误不会发生。 换句话说:在底层使用同步、原子操作或为您使用它们的类。

来源 http://docs.oracle.com/javase/specs/jls/se8/html/index.html Java®语言规范,2015-02-13

以我的理解,同步基本上意味着编译器编写一个监视器。进入并监控。在方法周围退出。因此,它可能是线程安全的,这取决于它是如何使用的(我的意思是,你可以编写一个具有同步方法的对象,它不是线程安全的,这取决于你的类做什么)。