Java锁的作用是什么?

  • Post category:Java

Java锁的作用是实现线程同步,主要目的是防止数据在多个线程同时读写时发生错误的情况。在多线程环境下,如果多个线程同时访问共享数据,可能会得到不一致的结果,这就是所谓的“线程安全问题”。使用锁可以避免这种问题,因为一旦线程加锁,其他线程就无法访问该对象的同步方法或代码块,直到该线程释放锁为止。

Java中提供了两种锁:synchronized关键字和ReentrantLock类,它们都可以达到同步的目的。其中synchronized关键字是Java中最基本的实现方式,可以用于同步方法和同步代码块。具体用法如下:

  • 同步方法:使用synchronized关键字修饰方法,表示该方法是同步方法,只能同时有一个线程执行该方法。示例代码如下:
public synchronized void syncMethod() {
    //业务代码
}
  • 同步代码块:使用synchronized关键字对代码块进行同步操作。示例代码如下:
public void syncCodeBlock() {
    synchronized(this) {
        //业务代码
    }
}

ReentrantLock类是Java提供的另一种锁实现方式,也可以达到同步的目的,但相比synchronized更加灵活并可以拥有更多的高级特性。ReentrantLock支持公平锁和非公平锁两种方式,允许中断等待中的线程、超时等待等高级特性。ReentrantLock的使用示例如下:

public class ReentrantLockDemo {
    private final ReentrantLock lock = new ReentrantLock();
    public void syncMethod() {
        lock.lock();
        try {
            //业务代码
        }finally {
            lock.unlock();
        }
    }
}

以上就是Java锁的作用及两种基本实现方式的详细讲解。

示例1:使用Java锁实现多线程读写同一个ArrayList,并查看结果的正确性

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ArrayListDemo {
    private static List<Integer> list = Collections.synchronizedList(new ArrayList<>());
    private static final int THREAD_COUNT = 1000;

    public static void main(String[] args) {
        // 启动1000个线程向ArrayList中添加数据
        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    list.add(j);
                }
            }).start();
        }

        // 主线程等待所有线程执行完毕
        while (Thread.activeCount() > 1) {
            Thread.yield();
        }

        // 输出List中的元素个数
        System.out.println(list.size());
    }
}

在不使用同步锁时,可能会输出的结果小于100000(即:线程不安全);当使用synchronized关键字修饰list.add()方法时,线程安全结果达到了100000个元素;当使用ReentrantLock时,线程安全结果同样达到了100000个元素。

示例2:使用Java锁解决线程安全问题,实现累加器

public class Counter {
    private int count;
    private final Object lock = new Object();

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

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

在多线程环境下使用Counter类时,如果不加锁,可能会得到错误的结果。当加上锁时,可达到正确的线程安全效果。

public static void main(String[] args) {
    Counter counter = new Counter();
    // 启动100个线程,每个线程执行100次累加操作
    for (int i = 0; i < 100; i++) {
        new Thread(() -> {
            for (int j = 0; j < 100; j++) {
                counter.increment();
            }
        }).start();
    }

    // 等待所有线程执行完毕
    while (Thread.activeCount() > 1) {
        Thread.yield();
    }

    // 输出结果
    System.out.println(counter.getCount());
}

以上代码中,Counter类的increment()方法和getCount()方法都使用synchronized关键字进行同步,确保了多个线程同时调用方法时的线程安全性。