使用synchronized来保证数据的同步

网上一搜索synchronized,资料大把大把,因此也不展开来讲,怕说多错多。简单说说个人理解和用法。java

synchronized是Java中的关键字,是一种同步锁。一般能够用来修饰某个方法,或者某个代码块。线程

我通常用来修饰代码块,感受会更加灵活。先上个例子:code

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。get

即下面例子全部的调用,都会等待。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的定义改成下面这种。io

private Object LOCK = new Object();

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

为何呢?由于每一个线程的LOCK再也不是同一个对象实例了。如今每一个LOCK都只属于它们的类实例。thread

这时,咱们能够这么改。搜索

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是否存在,而且是否已完成。

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

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

相关文章
相关标签/搜索