【高并发系列】8、synchronized关键字

线程安全是并行程序的根基;

volatile关键字只能保证一个线程修改数据后,其他线程可见这个修改;但两个或多个线程同时修改某一个数据时依然冲突;

以下例子最终结果小于20000000,同一时刻2个线程读取i的值后分别执行i++操作,虽然执行了2次i++操作,但i的值只增加了1次;

public class VolatileErrorDemo implements Runnable {
	private static VolatileErrorDemo instance = new VolatileErrorDemo();
	private static volatile int i = 0;
	public static void increase() {
		i++;
	}
	@Override
	public void run() {
		for (int j = 0; j < 10000000; j++) {
			increase();
		}
	}
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(instance);
		Thread t2 = new Thread(instance);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(i);
	}
}

synchronized关键字的作用是实现线程间的同步;用法如下:

  • 指定加锁对象:对给定对象加锁,进入同步代码前要获得指定对象的锁;
  • 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁;
  • 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁;

将synchronized关键字作用于给定对象实例instance,保证只有一个线程执行i++操作:

在main方法中创建线程时引用的是同一个instance实例,所以对于increase()方法加上synchronized关键字对当前实例加锁,实现线程同步;

public class SynchronizedDemo implements Runnable {
	private static SynchronizedDemo instance = new SynchronizedDemo();
	private static int i = 0;
	public synchronized void increase() {
		i++;
	}
	@Override
	public void run() {
		for (int j = 0; j < 10000000; j++) {
			increase();
		}
	}
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(instance);
		Thread t2 = new Thread(instance);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(i);
	}
}

或如下形式,等价代码如下:

public class SynchronizedDemo2 implements Runnable {
	private static SynchronizedDemo2 instance = new SynchronizedDemo2();
	private static int i = 0;
	@Override
	public void run() {
		for (int j = 0; j < 10000000; j++) {
			synchronized(instance) {
				i++;
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(instance);
		Thread t2 = new Thread(instance);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(i);
	}
}

如果不引用同一个Runnable实例,关注的是自己的对象锁,不能保证线程安全,需要在increase()方法上再增加static关键字,即使指向不同的Runnable实例,但静态方法是对于当前类加锁,而非当前实例,因此可以保证正确同步:

public class SynchronizedStaticDemo implements Runnable {
	private static int i = 0;
	public synchronized static void increase() {
		i++;
	}
	@Override
	public void run() {
		for (int j = 0; j < 10000000; j++) {
			increase();
		}
	}
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(new SynchronizedStaticDemo());
		Thread t2 = new Thread(new SynchronizedStaticDemo());
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(i);
	}
}

猜你喜欢

转载自blog.csdn.net/hellboy0621/article/details/86843900
今日推荐