“同步” 是什么意思?

我对synchronized关键字的用法和重要性有一些疑问。

  • synchronized关键字的意义是什么?
  • 方法应何时synchronized
  • 从程序上和逻辑上是什么意思?

答案

synchronized关键字是关于不同线程读写相同变量,对象和资源的全部内容。这不是 Java 中的琐碎话题,但以下是 Sun 的引文:

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

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

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

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

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);
    }
}
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

synchronized关键字可防止多个线程同时访问代码块或对象。默认情况下, Hashtablesynchronized ,因此一次只能有一个线程访问该表。

在使用non-synchronized结构(例如HashMap ,必须在代码中构建线程安全功能,以防止出现内存一致性错误。

synchronized是指在多线程环境中,具有synchronized方法 / 块的对象不允许两个线程同时访问代码的synchronized方法 / 块。这意味着一个线程无法读取,而另一个线程对其进行了更新。

相反,第二个线程将等到第一个线程完成其执行。开销是速度,但是好处是可以保证数据的一致性。

但是,如果您的应用程序是单线程的,则synchronized块不会带来任何好处。

if (vector.isEmpty()){
     vector.add(data);
 }

总览

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

sync 关键字用于定义代码块,多个线程可以安全地访问同一变量。

更深

语法明智的synchronized关键字接受一个Object ,因为它的参数( 称为锁定对象 ),其然后随后是{ block of code }

  • 当执行遇到此关键字时,当前线程将尝试 “锁定 / 获取 / 拥有”(选择) 锁定对象 ,并在获取锁定后执行关联的代码块。

  • 保证对同步代码块内的变量的任何写入均对使用相同锁对象类似地在同步代码块内执行代码的每个其他线程可见。

  • 一次只能有一个线程持有该锁,在此期间,所有其他尝试获取同一锁对象的线程都将等待(暂停执行)。当执行退出同步代码块时,该锁将被释放。

同步方法:

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

可以将其视为一种旋转门,就像您在足球场上可能会发现的那样。有很多人想进入,但在旋转门上他们是 “同步的”。一次只能一个人通过。所有想要通过的人都会做,但是他们可能必须等到可以通过。

什么是 synced 关键字?

线程主要通过共享对字段和对象引用字段所引用的访问进行通信。这种通信形式非常高效,但可能导致两种错误: 线程干扰和内存一致性错误 。防止这些错误的工具是同步。

同步块或方法可防止线程干扰并确保数据一致。在任何时间点,只有一个线程才能通过获取锁来访问同步的块或方法( 关键部分 )。其他线程将等待释放锁以访问关键节

方法何时同步?

将同步添加到方法定义或声明时,方法将synchronized 。您也可以使用方法来同步特定的代码块。

在程序上和逻辑上是什么意思?

这意味着只有一个线程可以通过获取锁来访问关键部分 。除非该线程释放此锁,否则所有其他线程将必须等待获取锁。他们无权进入没有获得锁定的关键部分

魔术无法做到这一点。程序员有责任识别应用程序中的关键部分并进行相应的保护。 Java 提供了一个框架来保护您的应用程序,但是所有需要保护的部分和位置由程序员负责。

来自 Java 文档页面的更多详细信息

内在锁和同步:

同步是围绕称为内部锁或监视器锁的内部实体构建的。内在锁在同步的两个方面都起作用:强制对对象状态的独占访问并建立对可见性至关重要的事前关联。

每个对象都有一个与之关联的固有锁 。按照约定,需要对对象的字段进行独占且一致的访问的线程必须在访问对象之前先获取对象的固有锁,然后在完成对它们的锁定后释放固有锁。

据说线程在获取锁和释放锁之间拥有内部锁。 只要一个线程拥有一个内在锁,其他任何线程都无法获得相同的锁。另一个线程在尝试获取锁时将阻塞。

当线程释放内在锁时,该动作与任何随后的相同锁获取之间将建立事前发生的关系。

使方法同步具有两个效果

首先,不可能对同一对象的两次同步方法调用进行交织。

当一个线程正在为对象执行同步方法时,所有其他为同一对象块调用同步方法的线程(挂起执行),直到第一个线程对该对象完成。

其次,当同步方法退出时,它会与随后对同一对象的同步方法的任何调用自动建立事前关联关系。

这样可以保证对对象状态的更改对所有线程都是可见的。

在以下位置查找同步的其他替代方法:

避免在 Java 中同步(this)?

这是Java 教程中的说明。

考虑以下代码:

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

如果countSynchronizedCounter的实例,则使这些方法同步具有两个效果:

  • 首先,不可能对同一对象的两次同步方法调用进行交织。当一个线程正在为对象执行同步方法时,所有其他为同一对象块调用同步方法的线程(挂起执行),直到第一个线程对该对象完成。
  • 其次,当同步方法退出时,它会与随后对同一对象的同步方法的任何调用自动建立事前关联关系。这样可以保证对对象状态的更改对所有线程都是可见的。
class A {
    public synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(this) {
             // all function code
        }
    } 
}
class A {
    public static synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(A.class) {
             // all function code
        }
    } 
}
class A {
    private Object lock1 = new Object();

    public void methodA() {
        synchronized(lock1 ) {
             // all function code
        }
    } 
}
synchronized(object or class) { // object/class use to provides the intrinsic lock
   // code 
}