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关键字进行同步,确保了多个线程同时调用方法时的线程安全性。