Java中对于并发问题的处理思路分享
多线程编程
在Java中,实现多线程编程主要通过 Thread
类或者实现 Runnable
接口来完成。创建和启动线程的方式有两种:
继承 Thread 类
class MyThread extends Thread {
@Override
public void run() {
// 线程执行逻辑
}
}
MyThread myThread = new MyThread();
myThread.start(); // 启动线程
实现 Runnable 接口
class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行逻辑
}
}
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start(); // 启动线程
synchronized 关键字
在多线程编程中,如果多个线程修改同一个数据,就会出现并发问题(比如线程A读取数据旧值,线程B修改了数据,线程A更新数据时使用错误的值)。为了避免这种情况,Java提供了 synchronized
关键字来进行同步锁,保证每次只有一个线程能够访问共享数据。
使用 synchronized 关键字的方式有两种:
同步方法
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
同步块
class Counter {
private int count = 0;
public void increment() {
synchronized(this) {
count++;
}
}
}
volatile 关键字
虽然 synchronized 关键字能够保证同步锁,但是由于Java使用了内存缓存,读写数据的时候可能会出现线程不安全。解决这种问题的方式是使用 volatile
关键字,它能保证每次读写数据时都直接从内存中访问,避免了缓存的问题。
class Counter {
private volatile int count = 0;
public void increment() {
count++;
}
}
示例
示例1:死锁问题
在以下代码中,ThreadA 和 ThreadB 分别占有 resA 和 resB 两个资源,但是它们在等待对方释放资源时都不会主动放弃资源,从而导致死锁问题。
public class DeadLockDemo {
private static Object resA = new Object();
private static Object resB = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (resA) {
System.out.println("ThreadA获得了resA锁。");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadA等待获取resB锁。。。");
synchronized (resB) {
System.out.println("ThreadA获得了resB锁。");
}
}
}, "ThreadA").start();
new Thread(() -> {
synchronized (resB) {
System.out.println("ThreadB获得了resB锁。");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadB等待获取resA锁。。。");
synchronized (resA) {
System.out.println("ThreadB获得了resA锁。");
}
}
}, "ThreadB").start();
}
}
解决死锁问题的方式是通过重试机制或者避免资源占用时间过长。
示例2:使用 CountDownLatch 来协调多个线程的执行
在以下代码中,有两个线程分别对一个数组进行求和,我们希望在两个线程都执行完之后输出最终结果。这时候可以使用 CountDownLatch
来实现线程的协作。
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
int[] nums = {1, 3, 5, 7, 9};
CountDownLatch latch = new CountDownLatch(2);
int mid = nums.length / 2;
AtomicInteger sum = new AtomicInteger();
new Thread(() -> {
for (int i = 0; i < mid; i++) {
sum.addAndGet(nums[i]);
}
latch.countDown();
}).start();
new Thread(() -> {
for (int i = mid; i < nums.length; i++) {
sum.addAndGet(nums[i]);
}
latch.countDown();
}).start();
latch.await();
System.out.println("最终结果:" + sum.get());
}
}
在例子中,我们使用 CountDownLatch
来控制线程数量,传入的参数为 2,表示有两个线程需要等待。线程执行完毕后,通过 latch.countDown()
来减少计数器的数量,最终当计数器数量为 0 时,主线程输出最终结果。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中对于并发问题的处理思路分享 - Python技术站