Java同步块
Java同步块语法
编写同步块的一般语法如下。
这里的 lockObject 是对一个对象的引用,该对象的锁与同步语句表示的监视器相关联。
synchronized( lockObject ) { // 同步语句 }
内部工作原理
当一个线程想要在同步块内执行同步语句时,它必须在 lockObject
的监视器上获取锁。
一次,只有一个线程可以获取锁对象的监视器。
所以所有其他线程必须等到这个线程,当前获取锁,完成它的执行。
这样,synchronized 关键字保证一次只有一个线程执行同步块语句,从而防止多个线程破坏块内的共享数据。
请记住,如果线程进入睡眠状态(使用 sleep()
方法),那么它不会释放锁。
在这个休眠时间,没有线程会执行同步块语句。
如果'synchronized (lock)'
中使用的锁对象为null
,则Java 同步将抛出NullPointerException。
Java 同步块示例
请注意,printNumbers() 方法中的代码位于 synchronized 块中。
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()
方法。
由于块被同步,只允许一个线程访问,其他线程必须等到第一个线程完成。
public class Main { public static void main(String args[]) { final MathClass mathClass = new MathClass(); // 第一个线程 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(); } }
输出:
ONE :: 1 ONE :: 2 ONE :: 3 TWO :: 1 TWO :: 2 TWO :: 3
Java同步方法
Java同步方法语法
编写同步方法的一般语法如下。
这里的 lockObject 是对一个对象的引用,该对象的锁与同步语句表示的监视器相关联。
<access modifier> synchronized method( parameters ) { // 同步代码 }
Java同步方法内部工作原理
与同步块类似,线程必须使用同步方法获取关联监视器对象上的锁。
在同步方法的情况下,锁对象是
- '.class' 对象 - 如果方法是静态的。
- 'this' 对象 - 如果该方法不是静态的。“this”指的是对调用同步方法的当前对象的引用。
Java synchronized 关键字本质上是可重入的,这意味着如果一个同步方法调用另一个需要相同锁的同步方法,那么当前持有锁的线程可以在不获取锁的情况下进入该方法。
Java同步方法示例
与同步块示例类似,我们可以在 printNumber()
方法中应用 synchronized 关键字,它将使该方法成为同步的。
现在,如果我们再次运行该示例,我们将得到类似的输出。
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); } } }
输出:
ONE :: 1 ONE :: 2 ONE :: 3 TWO :: 1 TWO :: 2 TWO :: 3
Java synchronized 关键字将块或者方法标记为临界区。
临界区是一次只有一个线程正在执行的地方,并且该线程持有同步部分的锁。
synchronized 关键字有助于编写应用程序的并发部分,以保护此块中的共享资源。
synchronized
关键字可用于
- 一个代码块
- 一个方法