# Java `synchronized`关键字

> 原文： [https://howtodoinjava.com/java/multi-threading/java-synchronized/](https://howtodoinjava.com/java/multi-threading/java-synchronized/)

**Java `synchronized`关键字**将块或方法标记为临界区。 临界区是一次仅执行一个线程的线程，该线程持有同步部分的锁。

`synchronized`关键字有助于编写应用程序的[并发](https://howtodoinjava.com/java-concurrency-tutorial/)部分，以保护此块中的共享资源。

`synchronized`关键字可以用于：

*   一个代码块
*   一个方法

## 1\. Java 同步块

#### 1.1 语法

编写同步块的一般语法如下。 此处 `lockObject`是对对象的引用，该对象的锁与同步语句表示的监视器关联。

```java
synchronized( lockObject ) 
{
   // synchronized statements
}

```

#### 1.2 内部工作

当线程要在同步块内执行同步语句时，必须获得`lockObject`监视器的锁定。 一次，只有一个线程可以获取锁对象的监视器。 因此，所有其他线程必须等到当前已获得锁的该线程完成执行。

通过这种方式，`synchronized`关键字可确保一次仅一个线程将执行同步的块语句，从而防止多个线程破坏该块内部的共享数据。

请记住，如果一个线程进入睡眠状态（使用`sleep()`方法），则它不会释放锁。 在此休眠时间，没有线程将执行同步的块语句。

如果`'synchronized (lock)'`中使用的锁定对象为`null`，则 Java 同步将抛出`NullPointerException`。

#### 1.3 Java 同步块示例

Java 程序演示同步块的用法。 在给定的示例中，我们具有使用方法`printNumbers()`的`MathClass`。 此方法将打印从 1 到参数 N 的数字。

请注意，`printNumbers()`方法中的代码在同步块内部。

```java
public class MathClass 
{
    void printNumbers(int n) throws InterruptedException 
    {
        synchronized (this) 
        {
            for (int i = 1; i <= n; i++) 
            {
                System.out.println(Thread.currentThread().getName() + " :: "+  i);
                Thread.sleep(500);
            }
        }
    }
}

```

我创建了两个线程，它们开始完全同时执行`printNumbers()`方法。 由于块被同步，因此仅允许一个线程访问，而另一个线程必须等待直到第一个线程完成。

```java
public class Main 
{
    public static void main(String args[]) 
    {
        final MathClass mathClass = new MathClass();

        //first thread
        Runnable r = new Runnable() 
        {
            public void run() 
            {
                try {
                    mathClass.printNumbers(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        new Thread(r, "ONE").start();
        new Thread(r, "TWO").start();
    }
}

```

程序输出。

```java
ONE :: 1
ONE :: 2
ONE :: 3

TWO :: 1
TWO :: 2
TWO :: 3

```

## 2\. Java 同步方法

#### 2.1 语法

编写同步方法的一般语法如下。 此处`lockObject`是对对象的引用，该对象的锁与同步语句表示的监视器关联。

```java
<access modifier> synchronized method( parameters ) 
{
    // synchronized code
}

```

#### 2.2 内部工作

与同步块类似，线程必须使用同步方法来获取关联的监视对象上的锁。 如果采用同步方法，则锁定对象为：

*   `.class`对象 - 如果该方法是静态的。
*   `this`对象 - 如果方法不是静态的。 `this`是指在其中调用同步方法的当前对象的引用。

> 阅读更多： [Java 中的对象级别锁与类级别锁](https://howtodoinjava.com/java/multi-threading/object-vs-class-level-locking/)

Java `synchronized`关键字本质上是**重入符**，这意味着如果同步方法调用了另一个需要相同锁定的同步方法，则持有锁定的当前线程可以进入该方法而无需获取锁定。

#### 2.3 Java 同步方法示例

与同步块示例类似，我们可以在`printNumber()`方法上应用`synchronized`关键字，这将使该方法成为同步方法。 现在，如果再次运行示例，我们将获得类似的输出。

```java
public class MathClass 
{
    synchronized void printNumbers(int n) throws InterruptedException 
    {
        for (int i = 1; i <= n; i++) 
        {
            System.out.println(Thread.currentThread().getName() + " :: "+  i);
            Thread.sleep(500);
        }
    }
}

```

程序输出：

```java
ONE :: 1
ONE :: 2
ONE :: 3

TWO :: 1
TWO :: 2
TWO :: 3

```

在评论中把您的问题交给我。

学习愉快！