本文共 4180 字,大约阅读时间需要 13 分钟。
线程安全是Java并发编程中的核心问题之一。在多线程环境下,共享资源的访问必须是安全的,避免数据竞争和脏读等问题。Java提供了两种关键工具来实现线程安全:volatile和synchronized。此外,Atomic系列(如AtomicInteger、AtomicLong等)提供了更高级别的原子操作,解决了volatile的局限性。
volatile的适用条件volatile关键字用于确保共享变量在所有线程中具有一致性,但其使用必须满足以下条件:
volatile的常见错误使用场景是像i++这样依赖于当前值的操作。多个线程同时读取并修改同一个计数器会导致丢失更新。例如,两个线程同时读取计数器的值8,并各自加一,结果会变成10,而不是预期的10次更新。
为了修复volatile的局限性,Java从1.5开始引入了原子变量和原子引用。AtomicBoolean、AtomicInteger、AtomicLong和AtomicReference等类提供了原子操作,它们不仅具有volatile的可见性,还允许修改操作依赖于当前值,避免了volatile的竞争问题。
以下是一个简单的线程安全类示例:
package com.boonya.thread;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;public class StateSafeThread extends Thread { private static final AtomicInteger atomicCount = new AtomicInteger(0); private static final AtomicInteger volatileAtomicCount = new AtomicInteger(0); @Override public void run() { final Object lock = new Object(); for (int i = 0; i < 50000; i++) { new Thread(new Runnable() { @Override public void run() { synchronized (lock) { // 不进行任何操作 } atomicCount.incrementAndGet(); volatileAtomicCount.incrementAndGet(); } }).start(); } try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread Name: " + this.getName()); System.out.println("线程并发执行对计数器累计5000次,请查看并发结果!"); System.out.println("atomicCount=" + atomicCount.get()); System.out.println("volatileAtomicCount=" + volatileAtomicCount.get()); }} 以下是一个线程池测试类示例:
package com.boonya.thread;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;import org.junit.Test;public class StateSafeThreadTest { @Test public void testByOneThread() { for (int i = 0; i < 5; i++) { StateSafeThread thread = new StateSafeThread(); thread.start(); } try { Thread.sleep(30000); } catch (InterruptedException e) { e.printStackTrace(); } } @Test public void testByThreadPool() { ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { StateSafeThread thread = new StateSafeThread(); executorService.execute(thread); } try { executorService.awaitTermination(30000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } finally { executorService.shutdown(); } } @Test public void testByThreadPool2() { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i < 5; i++) { StateSafeThread thread = new StateSafeThread(); executorService.execute(thread); } try { executorService.awaitTermination(30000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } finally { executorService.shutdown(); } } @Test public void testByThreadPool3() { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { StateSafeThread thread = new StateSafeThread(); executorService.execute(thread); } try { executorService.awaitTermination(30000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } finally { executorService.shutdown(); } }} 通过测试可以发现:
newFixedThreadPool(5)),则线程安全。newFixedThreadPool(3)),则线程不安全。newCachedThreadPool()默认创建新线程以应对高负载,同样需要线程安全类来确保正确性。通过合理选择线程池类型,可以确保线程安全类的正确运行。推荐使用缓存线程池以优化性能和资源利用率。
转载地址:http://jeyu.baihongyu.com/