返回

高级教程:synchronized 关键字——Java 多线程并发的可靠基石

后端

synchronized 的作用

synchronized 的作用主要是确保共享资源在多线程并发访问时保持线程安全性,防止出现竞争条件和数据不一致的问题。

原子性

所谓原子性就是指一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

public class Counter {
    private int count;

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

在这个例子中,increment() 方法被 synchronized 修饰,保证了对 count 的操作是原子的,即要么 count 的值加 1,要么 count 的值保持不变,不会出现 count 的值被多个线程同时修改的情况。

可见性

可见性是指一个线程对共享变量的修改能够及时地反映到其他线程中,即对共享变量的修改对其他线程是可见的。

public class VisibilityExample {
    private boolean flag = false;

    public void setFlag() {
        flag = true;
    }

    public boolean getFlag() {
        return flag;
    }
}

在这个例子中,由于 flag 变量没有被 synchronized 修饰,因此对 flag 的修改可能不会及时地反映到其他线程中,可能出现其他线程读取到旧的 flag 值的情况。

有序性

有序性是指对共享变量的修改按照程序执行的顺序执行,不会出现指令重排的情况。

public class OrderExample {
    private int x = 0;
    private int y = 0;

    public void setX() {
        x = 1;
    }

    public void setY() {
        y = 1;
    }

    public void print() {
        System.out.println("x = " + x + ", y = " + y);
    }
}

在这个例子中,由于 setX() 和 setY() 方法没有被 synchronized 修饰,因此可能出现 x 和 y 的值不一致的情况,即 print() 方法可能打印出 x = 1, y = 0 或 x = 0, y = 1。

synchronized 的用法

synchronized 关键字可以修饰方法或代码块,当一个线程访问被 synchronized 修饰的方法或代码块时,必须先获取锁,才能执行该方法或代码块,其他线程必须等待该线程释放锁才能继续执行。

public class Counter {
    private int count;

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

在这个例子中,increment() 方法被 synchronized 修饰,因此当一个线程访问 increment() 方法时,必须先获取锁,才能执行该方法,其他线程必须等待该线程释放锁才能继续执行。这样就保证了对 count 的操作是原子的,即要么 count 的值加 1,要么 count 的值保持不变,不会出现 count 的值被多个线程同时修改的情况。

synchronized 的注意事项

在使用 synchronized 关键字时,需要考虑以下注意事项:

  • 尽量避免使用 synchronized 修饰整个方法,而应该只修饰需要同步访问的代码块。这样可以减少锁的持有时间,提高并发的性能。
  • 避免在 synchronized 代码块中执行耗时的操作,否则会影响其他线程的执行。
  • 尽量避免在 synchronized 代码块中调用其他 synchronized 方法,否则可能导致死锁。

总结

synchronized 关键字是 Java 并发编程中最重要的工具之一,用于控制多线程访问共享资源,确保线程安全性。通过理解 synchronized 的作用、原理和用法,可以帮助您编写出安全可靠的多线程程序。