Java并发编程——共享资源竞争问题

案例分析

创建两个线程 t1 、t2 ,t1 线程负责变量 count 自增 5000 次,t2 线程负责变量 count 自减 5000 次

public class AddAndSub {
	static int count = 0;

	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 5000; i++) {
					count++;
				}
			}
		};

		Thread t2 = new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 5000; i++) {
					count--;
				}
			}
		};

		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(count);
	}
}

由于线程执行的顺序不受控制,可能会发生如下情况:

假设此时 count 值为 0 ,t1 线程读取到 count 的值,进行自增后变成 1 ,还没来得及写入 count ,t2 线程就读取了 count 原来的值 0 ,进行自减后,将 -1 写入 count ,然后 t1 线程将刚刚自增得到的结果 1 写入 count ,最终导致了分别进行一次自增自减后,count 的值从 0 变成了 1


概念介绍

临界资源

一次仅允许一个进程使用的共享资源,如物理设备中的打印机、输入机和进程之间共享的变量、数据

临界区(Critical Section)

每个进程中访问临界资源的代码段,如上述案例中的 count++

竟态条件(Race Condition)

两个或者多个进程竞争使用临界资源,这些进程可能因为执行先后顺序的不受控制而出现问题,如上述案例中的 t1 和 t2 线程执行顺序不受控制,导致结果错误


synchronized 实现互斥

static Object lock = new Object();

synchronized (lock) {
	count++;
}

synchronized (lock) {
	count--;
}

使用 synchronized 关键字对 lock 对象加锁,当其中一个线程对 count 进行自增或自减操作时,另外的线程由于获取不到 lock 锁,被阻塞,保证了临界区内代码的原子性


封装共享资源

public class AddAndSubOptimize {
	public static void main(String[] args) throws InterruptedException {
		Resource resource = new Resource();
		Thread t1 = new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 5000; i++) {
					resource.increment();
				}
			}
		};

		Thread t2 = new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 5000; i++) {
					resource.decrement();
				}
			}
		};

		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(resource.getCount());
	}
}

class Resource {
	private int count = 0;

	public void increment() {
		synchronized (this) {
			count++;
		}
	}

	public void decrement() {
		synchronized (this) {
			count--;
		}
	}

	public int getCount() {
		synchronized (this) {
			return count;
		}
	}
}

通过 Resource 类封装了共享资源(变量)count ,对变量操作的互斥逻辑在类的内部实现,外部只需要调用相应的方法即可

猜你喜欢

转载自blog.csdn.net/qq_25274377/article/details/120603531