深入解析Java并发程序中线程的同步与线程锁的使用
在Java并发程序中,线程的同步和线程锁是必不可少的。线程的同步是指多个线程协同工作,按一定的顺序执行,而线程锁则是保证多个线程访问共享资源时数据的正确性和一致性。
线程同步
Java中线程同步的主要方式有以下两种:
1. synchronized关键字
synchronized
关键字可以修饰方法和代码块,用来实现线程同步。当一个线程访问一个被synchronized
关键字修饰的方法或代码块时,其他线程只能等待,直到当前线程执行完了才能继续执行。
下面是一个使用synchronized
修饰方法的示例:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized void decrement() {
count--;
}
public synchronized int getCount() {
return count;
}
}
在上述代码中,synchronized
关键字修饰了三个方法,因此这三个方法都是同步的。多个线程同时访问这个类的实例时,每个线程会依次执行这三个方法。由于这三个方法都是同步的,因此保证了线程的安全性。
2. Lock接口
Java中的Lock
接口是一个更加灵活和强大的线程同步机制。与synchronized
相比,它提供了更细粒度的锁控制,例如可重入锁、读写锁、条件锁等。
下面是一个使用可重入锁的示例:
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
count--;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
在上述代码中,使用了可重入锁来实现线程的同步。在每个方法中,需要先获取锁,执行完后再释放锁。由于锁是可重入的,因此在同一线程中可以重复获取锁。
线程锁
线程锁主要用于保证访问共享资源时的数据一致性。常见的线程锁有以下两种:
1. 实例锁
实例锁即是基于对象的锁,每个对象都对应着一把锁。当一个线程获取了对象的锁后,其他线程就必须等待,直到该线程释放锁后才能访问该对象上的其他同步方法和代码块。
下面是一个实例锁的示例:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized void decrement() {
count--;
}
public synchronized int getCount() {
return count;
}
}
在上述代码中,synchronized
关键字修饰的三个方法实际上是基于对象的锁。每个Counter实例都有一个锁,当一个线程获取锁时,其他线程就必须等待。
2. 类锁
类锁是基于类的锁,是与类的Class对象相关联的锁。当一个线程获取了类锁后,其他线程对该类上的同步方法和同步代码块的访问也被限制。
下面是一个使用类锁的示例:
public class Counter {
private static int count = 0;
public static synchronized void increment() {
count++;
}
public static synchronized void decrement() {
count--;
}
public static synchronized int getCount() {
return count;
}
}
在上述代码中,synchronized
关键字修饰的三个方法实际上是基于类的锁。当一个线程获取了类锁时,其他线程对该类上的同步方法和同步代码块的访问也被限制。
示例说明
下面是一个使用线程同步和线程锁的完整示例,该示例演示了一种通过多线程增加计数器的方法,使用线程同步和线程锁来保证计数器的正确性和一致性。
import java.util.ArrayList;
import java.util.List;
public class Main {
private static final int THREAD_NUM = 10;
private static final int LOOP_NUM = 10000;
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
List<Thread> threads = new ArrayList<>(THREAD_NUM);
for (int i = 0; i < THREAD_NUM; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < LOOP_NUM; j++) {
counter.increment();
}
}
});
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println("Final Count: " + counter.getCount());
}
}
在上述代码中,我们定义了一个Counter
类,包含了一个计数器和对计数器进行增减操作的方法。在main
方法中,我们使用10个线程同时对计数器进行增加操作,每个线程增加10000次。由于多个线程同时操作计数器,因此需要使用线程同步和线程锁。
在Counter
类中,我们使用synchronized
关键字和ReentrantLock
来实现线程同步,实例锁和类锁来实现线程锁。
通过运行这个程序,可以发现最终的计数器的值为100000
,说明计数器的增加操作是正确和一致的。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:深入解析Java并发程序中线程的同步与线程锁的使用 - Python技术站