当多个线程需要共同访问一个共享资源时,由于线程执行的随机性,很容易遇到竞态条件的问题。Java提供了锁机制来解决这一问题,锁可用于在多线程访问共享资源时控制对资源的访问。
在Java中,锁机制可以使用synchronized
关键字或java.util.concurrent
包中的Lock
接口实现。
使用synchronized关键字实现锁
synchronized
关键字可以用于方法或块的级别以达到锁的效果。当一个线程访问一个被synchronized
关键字保护的代码块时,其他线程会被阻塞,直到该线程完成该代码块的执行。
public class SynchronizedDemo {
private int count;
public synchronized void increment() {
count++;
}
}
在上述例子中,当多个线程访问increment()
方法时,只有一个线程可以执行该方法,其他线程将被阻塞。因为synchronized
关键字的使用,保证了count++
操作的原子性,避免了竞争条件。
使用Lock接口实现锁
Lock
接口是一种更灵活的锁,相对于synchronized
关键字来说,它提供了更多控制多线程访问共享资源的方式。在使用Lock
时,需要调用lock()
方法来获取锁,使用完毕后需要调用unlock()
方法释放锁。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
private Lock lock = new ReentrantLock(); // 创建ReentrantLock锁
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 释放锁
}
}
}
在上述例子中,线程需要使用lock()
方法获取锁,而不是快捷方式synchronized
。并且需要在使用完锁之后调用unlock()
方法释放锁,以便其他线程进行访问。
示例
Synchronized锁
下面的示例演示了如何使用synchronized
关键字来实现一个线程安全的计数器。
public class SynchronizedCounter {
private int count = 0;
public synchronized int increment() {
count++;
return count;
}
public synchronized int decrement() {
count--;
return count;
}
public synchronized int getCount() {
return count;
}
}
在上述例子中,increment()
、decrement()
、getCount()
方法都使用synchronized
关键字,以便避免多个线程同时对count
变量进行读写。
Lock锁
下面的示例演示了如何使用Lock
接口来实现一个线程安全的计数器。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockCounter {
private int count = 0;
private Lock lock = new ReentrantLock();
public int increment() {
lock.lock();
try {
count++;
return count;
} finally {
lock.unlock();
}
}
public int decrement() {
lock.lock();
try {
count--;
return count;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
在上述示例中,与synchronized
关键字不同,使用Lock
锁需要在try-finally
代码块中包装获取锁和释放锁的过程。在获取锁失败的情况下,finally
中的代码块将负责释放锁。