深入了解Java中Synchronized的各种使用方法
在 Java 中,Synchronized 是一种保证多线程访问同一个共享资源时,只有一个线程可以进入代码块,从而保证线程安全的关键字。这篇文章将深入讲解 Java 中 Synchronized 的各种使用方法,例如对象锁、类锁和非阻塞同步等。
对象锁
对象锁是指用 Synchronized 关键字修饰的代码块,将拥有该对象的锁,同一时间只能有一个线程进入该对象的代码块。例如下面这个示例:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized void decrement() {
count--;
}
public int getCount() {
return count;
}
}
在上述示例中,Counter 类中的 increment 和 decrement 方法用了Synchronized 关键字,由此得到了“counter”对象的锁,而 getCount 方法没有使用 Synchronized 关键字,所以多个线程可以同时访问 getCount 方法。
类锁
类锁是指用 Synchronized 关键字修饰的静态方法,将拥有该类的锁,同一时间只能有一个线程进入该类的代码块。例如下面这个示例:
class Counter {
private static int count = 0;
public static synchronized void increment() {
count++;
}
public static synchronized void decrement() {
count--;
}
public static int getCount() {
return count;
}
}
在上述示例中,Counter 类中的 increment 和 decrement 方法同样用了 Synchronized关键字,但是由于这两个方法是静态方法,所以Synchronized 关键字将获得 Counter 类的锁。
非阻塞同步
非阻塞同步是指用 Synchronized 关键字修饰的代码块中,加锁和解锁都是采用 CAS(Compare And Swap)算法实现的同步方式。例如下面这个示例:
import java.util.concurrent.atomic.AtomicInteger;
class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
while (true) {
int oldCount = count.get();
int newCount = oldCount + 1;
if (count.compareAndSet(oldCount, newCount)) {
break;
}
}
}
public int getCount() {
return count.get();
}
}
在上述示例中,Counter 类中的 increment 方法使用了 AtomicInteger 类来替代基础数据类型,采用了 CAS 算法进行同步,达到了非阻塞同步的目的。这种方法虽然比较复杂,但是可以避免锁竞争和死锁。
示例说明
下面是一个相关示例的解释。
例如,我们有这么一段代码:
public class ThreadSafeExample{
private String name;
public ThreadSafeExample(String name){
this.name = name;
}
public synchronized void printString(){
System.out.println("Begin");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End");
}
}
这里,我们有一个线程安全的类 ThreadSafeExample,它有一个同步方法 printString,该方法打印一条字符串并休眠 5 秒钟。
然后,我们再来创建两个类:
public class ThreadA extends Thread {
private ThreadSafeExample threadSafeExample;
public ThreadA(ThreadSafeExample threadSafeExample){
this.threadSafeExample = threadSafeExample;
}
public void run(){
threadSafeExample.printString();
}
}
public class ThreadB extends Thread {
private ThreadSafeExample threadSafeExample;
public ThreadB(ThreadSafeExample threadSafeExample){
this.threadSafeExample = threadSafeExample;
}
public void run(){
threadSafeExample.printString();
}
}
ThreadA 和 ThreadB 都是通过接收一个 ThreadSafeExample 类型的参数来初始化的。此外,它们都有一个 run 方法,该方法会调用 printString 方法。
现在,我们来看看如果我们同时启动这两个线程会发生什么:
public class ThreadSafeTest {
public static void main(String[] args) {
ThreadSafeExample threadSafeExample = new ThreadSafeExample("ThreadSafe");
ThreadA threadA = new ThreadA(threadSafeExample);
ThreadB threadB = new ThreadB(threadSafeExample);
threadA.start();
threadB.start();
}
}
如果你运行上面的代码,你会看到以下输出:
Begin
Begin
End
End
这是因为 printString 方法是同步方法,并且两个线程共享了一个 ThreadSafeExample 实例。因此,它们必须按顺序访问该实例的 synchronized 方法。所以,线程 A 先获取了该实例的锁,然后开始执行 printString 方法。而在等待 5 秒的过程中,线程 B 要等待线程 A 释放该实例的锁,才能获取该实例的锁开始执行 printString 方法。
综上所述,Synchronized 是 Java 中保证线程安全的关键字。通过对不同类型 Synchronized 的使用方法的了解,我们可以更好地实现线程安全的程序。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:深入了解Java中Synchronized的各种使用方法 - Python技术站