使用synchronized来保证数据的同步

China☆狼群 提交于 2019-12-06 02:03:30

网上一搜索synchronized,资料大把大把,所以也不展开来说,怕说多错多。简单说说我的理解和用法。

synchronized是Java中的关键字,是一种同步锁。通常可以用来修饰某个方法,或者某个代码块。

我一般用来修饰代码块,感觉会更加灵活。先上个例子:

public class Task implements Runnable{

	private static final Object LOCK = new Object();
	
	public void run(){
		long threadId = Thread.currentThread().getId();
		System.out.println(threadId + " 等待执行");
		synchronized (LOCK) {
			try {
				System.out.println(threadId + " 执行中");
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(threadId + " 执行结束");
		}
	}
}

首先我们定义了一个常量LOCK,执行到这个synchronized 代码块的时候,都会先检查是否获得LOCK的使用权。

由于LOCK是全局的常量,所以所有线程拿到的都是同一个LOCK对象,所以所有线程在到这里的时候,都需要排队等LOCK。

即下面例子所有的调用,都会等待。PS.你也可以去掉synchronized关键词来看下另外一种执行结果。

	public static void main(String[] args) {
		ExecutorService service = Executors.newFixedThreadPool(10);
		for (int i=0; i<10; i++) {
			service.execute(new Task());
		}
	}

如果LOCK的定义改为下面这种。

private Object LOCK = new Object();

再执行上述的main方法,会发现,代码块里面的内容不再等待了。当一个线程执行时,另外一个线程也开始执行了。

为什么呢?因为每个线程的LOCK不再是同一个对象实例了。现在每个LOCK都只属于它们的类实例。

这时,我们可以这么改。

	public static void main(String[] args) {
		ExecutorService service = Executors.newFixedThreadPool(10);
		Task task = new Task();
		for (int i=0; i<10; i++) {
			service.execute(task);
		}
	}

对于每个线程来说,LOCK都是同一个锁,所以线程间会进行等待。

总结一句:synchronized修饰代码块的时候,可以让使用了同一个锁对象实例的多个线程进行排队等待执行。

再补充一个,使用synchronized的时候,经常要使用到双重检查。还是直接举例子吧。

假设我们有个将任务修改为完成的功能,修改为完成状态之后,还要通知发任务的用户。如果不使用同步锁进行控制,多人触发同一个任务的完成操作时,可能会出现多次通知同一个用户的情况。这时,我们可以使用synchronized来防止这个情况。代码大概是这样的。

	public void completeTask(long taskId) {
		Task task = Task.get(taskId);
		if(task!=null && task.hasCompleted()) {
			return;
		}
		synchronized (task) {
			task = Task.get(taskId);
			if(task!=null && task.hasCompleted()) {
				return;
			}
			Task.complete(task);
			NoticeUtil.noticeTaskComplete(task.getPublisher());
		}
	}

一开始,我们获取了一个task实例,这里,我们必须保证对于相同taskId的task必须是同一个实例对象,synchronized才会有效。

拿到task之后,我们先检查task是否存在,并且是否已完成。

然后会进行排队,排队之后,我们会再次进行上述检查。这两次检查,就是双重功能检查的意思。可以避免出现多个线程重复执行某段代码。假设没有第二次检查,则排队线程获得锁的时候,一样会有“通知用户”的行为。

表达能力有限,不知道上述内容有没有把我表达的意思说清楚。希望对看到本文的你有所帮助:-)。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!