上一篇文章提到了并发编程中常见的问题,饥饿、死锁以及竞争条件。本文就尝试解决这些问题。每一种解决问题的方法都有其适用的场景,在使用方法之前,我们需要了解其适用场景。
public class Test {
private static int count = 0; // 共享变量
private static CountDownLatch latch = new CountDownLatch(100);
/*public static int getCount(final int scale) {
count += scale;
return count;
}
public static void setCount(int count) {
Test.count = count;
}*/
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
try {
for (int i = 1; i <= 100; i++) {
final int scale = i;
executorService.submit(new Runnable() {
@Override
public void run() {
//getCount(scale);
synchronized (Test.class) {
count += scale;
}
try {
TimeUnit.SECONDS.sleep(1); // 模拟线程执行的长时任务
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}
});
} // end for
}finally {
executorService.shutdown();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count);
}
}
上述的实例中,count是一个共享变量,即每一个线程都可以更改count的值,而且在同一时刻可能会有多个线程同时更改count值,共享可变性会造成同步问题,使结果不准确。上述实例模拟了计算1-100之和,开启了100个线程,最终的结果应为:5050。
在没有加synchronized 同步块时,结果如下:
image.png
每次的结果都不一定相同。这就是竞争条件带来的问题。
解决方法一:使用synchronized关键字来显示地获取对象的monitor/锁。 锁机制实现同步。同一时刻最多只有一个线程执行加synchronized 的代码。
解决方法二:使用java.util.concurrent.atomic包中定义的原子操作类。原子操作类对于处理单个共享数据的值来说非常有用。如下实例2
实例2
public class Test {
private static AtomicInteger count = new AtomicInteger(0); // 共享变量
private static CountDownLatch latch = new CountDownLatch(100);
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
try {
for (int i = 1; i <= 100; i++) {
final int scale = i;
executorService.submit(new Runnable() {
@Override
public void run() {
count.addAndGet(scale);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}
});
} // end for
}finally {
executorService.shutdown();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count.get());
}
}











网友评论