Java中的synchronized有几种加锁方式(实例详解)
作者:TS86
在Java中,synchronized
关键字提供了内置的支持来实现同步访问共享资源,以避免并发问题。synchronized
主要有三种加锁方式:
1.同步实例方法
当一个实例方法被声明为synchronized
时,该方法将同一时间只能被一个线程访问。锁是当前对象实例(即this
)。
public class SynchronizedInstanceMethod { public synchronized void doSomething() { // 同步代码块 // 同一时间只能有一个线程执行这里的代码 } public static void main(String[] args) { SynchronizedInstanceMethod obj = new SynchronizedInstanceMethod(); // 创建多个线程访问obj的doSomething方法,它们将串行执行 // ... } }
当我们创建了两个线程来并发地增加计数器,由于我们使用了synchronized
,因此计数器的增加线程是安全的,即使两个线程都在尝试修改同一个共享变量。在同步实例方法中,锁分别是实例对象和类对象。
public class SynchronizedInstanceMethodExample { private int count = 0; // 同步实例方法,锁定的是当前对象的实例(this) public synchronized void increment() { count++; System.out.println(Thread.currentThread().getName() + " incremented count to " + count); } public static void main(String[] args) { final SynchronizedInstanceMethodExample example = new SynchronizedInstanceMethodExample(); // 创建两个线程来增加计数器 Thread thread1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }, "Thread-1"); Thread thread2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }, "Thread-2"); // 启动线程 thread1.start(); thread2.start(); // 等待线程完成 try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } // 输出最终计数 System.out.println("Final count: " + example.count); } }
2.同步静态方法
当一个静态方法被声明为synchronized
时,该方法将同一时间只能被一个线程访问。锁是Class对象,而不是实例对象。
public class SynchronizedStaticMethod { public static synchronized void doSomethingStatic() { // 同步代码块 // 同一时间只能有一个线程执行这里的代码 } public static void main(String[] args) { // 创建多个线程访问SynchronizedStaticMethod的doSomethingStatic方法,它们将串行执行 // ... } }
当我们创建了两个线程来并发地增加计数器,由于我们使用了synchronized
,因此计数器的增加线程是安全的,即使两个线程都在尝试修改同一个共享变量。在同步静态方法中,锁分别也是实例对象和类对象。
public class SynchronizedStaticMethodExample { private static int count = 0; // 同步静态方法,锁定的是当前对象的类(Class)对象 public static synchronized void increment() { count++; System.out.println(Thread.currentThread().getName() + " incremented static count to " + count); } public static void main(String[] args) { // 创建两个线程来增加计数器 Thread thread1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { increment(); } }, "Thread-1"); Thread thread2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { increment(); } }, "Thread-2"); // 启动线程... // 等待线程完成... // 输出最终计数... // (与上面的例子类似,但省略了重复的代码) } }
3.同步代码块
我们可以使用synchronized
关键字来定义一个代码块,而不是整个方法。在这种情况下,你可以指定要获取的锁对象。这提供了更细粒度的同步控制。
public class SynchronizedBlock { private final Object lock = new Object(); // 用于同步的锁对象 public void doSomething() { synchronized (lock) { // 同步代码块 // 同一时间只有一个线程能够执行这里的代码 } // 这里的代码不受同步代码块的约束 } public static void main(String[] args) { SynchronizedBlock obj = new SynchronizedBlock(); // 创建多个线程访问obj的doSomething方法,但只有在synchronized块中的代码将串行执行 // ... } }
在上面的SynchronizedBlock
类中,我们创建了一个私有的Object
实例lock
作为锁对象。当线程进入synchronized (lock)
块时,它会尝试获取lock
对象的锁。如果锁已经被其他线程持有,那么该线程将被阻塞,直到锁被释放。
当我们创建了两个线程来并发地增加计数器,同步代码块的例子中,我们显式地指定了一个对象作为锁。
public class SynchronizedBlockExample { private final Object lock = new Object(); // 用于同步的锁对象 private int count = 0; // 同步代码块,指定了锁对象 public void increment() { synchronized (lock) { count++; System.out.println(Thread.currentThread().getName() + " incremented count to " + count); } } public static void main(String[] args) { // 类似于上面的例子,但使用SynchronizedBlockExample的increment方法 // ...(省略了重复的代码) } }
注意:使用synchronized
时应该尽量避免在持有锁的情况下执行耗时的操作,因为这会导致其他等待锁的线程长时间阻塞。同时,过度使用synchronized
可能会导致性能下降,因为它会引入线程间的竞争和可能的上下文切换。在设计并发程序时,应该仔细考虑同步的粒度,并可能使用其他并发工具(如ReentrantLock
、Semaphore
、CountDownLatch
等)来提供更细粒度的控制。
到此这篇关于java的synchronized有几种加锁方式的文章就介绍到这了,更多相关java synchronized加锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!