【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解-LMLPHP

目录

一、synchronized 

1.线程不安全实例

2.线程安全实例

3.synchronized修饰代码块

二、ReentrantLock


一、synchronized 

Synchronized是 Java 中的一个关键字,用于实现线程同步。它可以修饰方法或代码块,确保在同一时间只有一个线程可以执行被修饰的代码。
Synchronized是一种互斥锁,也称为悲观锁。它的原理是在执行被修饰的代码之前,线程会尝试获取锁。如果锁已经被其他线程持有,那么当前线程将被阻塞,直到锁被释放。

注意:在实际应用中,需要根据具体情况合理使用Synchronized锁,避免过度使用导致性能下降。

1.线程不安全实例

public class UnsafeCounter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

假设有两个线程同时调用 increment() 方法增加计数:

public class UnsafeCounterExample {
    public static void main(String[] args) {
        UnsafeCounter counter = new UnsafeCounter();

        // 创建两个线程并发增加计数
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        thread1.start();
        thread2.start();

        // 等待两个线程执行完成
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出最终计数值
        System.out.println("Count: " + counter.getCount()); // 预期结果: 可能小于 2000
    }
}

在这个示例中,由于 increment() 方法没有同步控制,两个线程同时对 count 进行增加操作,可能导致计数不准确。理论上来说,代码应该执行的结果是:2000,但是因为线程不安全,就会导致数据不正确!

第一次运行:

【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解-LMLPHP

第二次运行:

【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解-LMLPHP

看到没,结果都会不一样,这要是正式环境中金额的话,那就不得了啦。

2.线程安全实例

使用synchronized修复线程安全问题。

public class SafeCounter {
    private int count = 0;

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

    public synchronized int getCount() {
        return count;
    }
}

在 SafeCounter 类中,我们使用 synchronized 关键字修饰了 increment() 和 getCount() 方法,确保同一时刻只有一个线程可以执行这些方法。

public class SafeCounterExample {
    public static void main(String[] args) {
        SafeCounter counter = new SafeCounter();

        // 创建两个线程并发增加计数
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        thread1.start();
        thread2.start();

        // 等待两个线程执行完成
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出最终计数值
        System.out.println("Count: " + counter.getCount()); // 预期结果: 2000
    }
}

在这个示例中,由于使用了 synchronized 关键字修饰方法,保证了线程安全,最终输出的计数值是准确的。

第一次运行:

【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解-LMLPHP

第二次运行:

【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解-LMLPHP

可以很清楚的看到,正确的数据是:2000。上面的是synchronized直接修饰了方法,那么接下来使用synchronized来修饰代码块来实现!

3.synchronized修饰代码块

public class SafeCounter {
    private int count = 0;

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

    public int getCount() {
        synchronized (this) {
            return count;
        }
    }
}

在这个示例中,我们使用了 synchronized 关键字来修饰 increment() 和 getCount() 方法中的代码块,同时传入了 this 作为锁对象。这样就确保了在同一时刻只有一个线程可以访问被 synchronized 修饰的代码块,从而实现了线程安全。

public class SafeCounterExample {
    public static void main(String[] args) {
        SafeCounter counter = new SafeCounter();

        // 创建两个线程并发增加计数
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        thread1.start();
        thread2.start();

        // 等待两个线程执行完成
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出最终计数值
        System.out.println("Count: " + counter.getCount()); // 预期结果: 2000
    }
}

第一次运行:

【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解-LMLPHP

第二次运行:

【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解-LMLPHP

需要注意:

synchronized (this)适用场景:

  1. 当需要在多个线程间同步访问对象实例的非静态方法或成员变量时,可以使用 synchronized (this) 来锁定当前对象实例,确保同一时刻只有一个线程访问对象的方法或成员变量。
  2. 示例:在多线程环境下,如果有多个线程同时操作同一个对象实例的方法或成员变量时,可以使用 synchronized (this) 来确保线程安全。

synchronized (SafeCounter.class)适用场景

  1. 当需要在多个线程间同步访问类的静态方法或静态变量时,可以使用 synchronized (SafeCounter.class) 来锁定类对象,确保同一时刻只有一个线程访问类的静态方法或静态变量。
  2. 示例:在多线程环境下,如果有多个线程同时调用同一个类的静态方法或静态变量时,可以使用 synchronized (SafeCounter.class) 来确保线程安全。

二、ReentrantLock

今天没时间了,明天待续!

05-09 12:00