竞态条件

竞态条件(Race Condition)发生在多个线程同时访问共享资源,且最终结果依赖于线程执行的顺序。由于线程调度的不确定性,可能导致程序行为不一致或错误。

public class RaceConditionExample {
    private int counter = 0;

    public void increment() {
        counter++;
    }

    public int getCounter() {
        return counter;
    }
}

在上述代码中,如果多个线程同时调用 increment() 方法,可能会导致 counter 的值不准确。解决方法是使用同步机制,如 synchronized 关键字或 ReentrantLock。

public class RaceConditionExample {
    private int counter = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            counter++;
        }
    }

    public int getCounter() {
        return counter;
    }
}

死锁

死锁(Deadlock)发生在两个或多个线程相互等待对方释放锁,导致所有线程都无法继续执行。死锁通常涉及多个锁资源。

public class DeadlockExample {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    public void method1() {
        synchronized (lock1) {
            synchronized (lock2) {
                // 执行操作
            }
        }
    }

    public void method2() {
        synchronized (lock2) {
            synchronized (lock1) {
                // 执行操作
            }
        }
    }
}

在上述代码中,如果线程 A 调用 method1() 并持有 lock1,同时线程 B 调用 method2() 并持有 lock2,那么两个线程将相互等待,导致死锁。避免死锁的方法包括按顺序获取锁、使用超时机制或避免嵌套锁。

活锁

活锁(Livelock)发生在线程不断尝试解决某个问题,但由于相互干扰,导致无法取得进展。活锁与死锁不同,线程并未被阻塞,而是处于忙碌状态。

public class LivelockExample {
    private boolean sharedResource = false;

    public void method1() {
        while (!sharedResource) {
            // 尝试获取资源
            sharedResource = true;
        }
    }

    public void method2() {
        while (sharedResource) {
            // 尝试释放资源
            sharedResource = false;
        }
    }
}

在上述代码中,如果两个线程分别调用 method1() 和 method2(),它们可能会不断切换 sharedResource 的状态,导致活锁。解决活锁的方法包括引入随机延迟或重新设计线程的协作逻辑。

线程饥饿

线程饥饿(Thread Starvation)发生在某些线程由于优先级低或资源分配不均,长时间无法获得执行机会。线程饥饿通常与线程调度策略或资源竞争有关。

public class StarvationExample {
    private final Object lock = new Object();

    public void method() {
        synchronized (lock) {
            // 长时间操作
        }
    }
}

在上述代码中,如果一个线程长时间持有锁,其他线程可能会因为无法获取锁而处于饥饿状态。解决线程饥饿的方法包括使用公平锁、减少锁的持有时间或调整线程优先级。

内存可见性

内存可见性(Memory Visibility)问题发生在多个线程访问共享变量时,由于线程本地缓存或编译器优化,导致一个线程对变量的修改对其他线程不可见。

public class VisibilityExample {
    private boolean flag = false;

    public void setFlag() {
        flag = true;
    }

    public boolean getFlag() {
        return flag;
    }
}

在上述代码中,如果一个线程调用 setFlag() 方法,另一个线程调用 getFlag() 方法,可能会由于内存可见性问题导致后者无法看到 flag 的更新。解决方法是使用 volatile 关键字或同步机制。

public class VisibilityExample {
    private volatile boolean flag = false;

    public void setFlag() {
        flag = true;
    }

    public boolean getFlag() {
        return flag;
    }
}

上下文切换开销

上下文切换(Context Switching)是操作系统在多线程环境下切换线程执行的过程。频繁的上下文切换会导致性能下降,尤其是在线程数量较多时。

public class ContextSwitchExample {
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                // 执行操作
            }).start();
        }
    }
}

在上述代码中,创建大量线程会导致频繁的上下文切换,影响程序性能。优化方法包括使用线程池、减少线程数量或使用异步编程模型。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ContextSwitchExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            executor.submit(() -> {
                // 执行操作
            });
        }
        executor.shutdown();
    }
}

通过使用线程池,可以有效减少上下文切换的开销,提高程序性能。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐