网上一搜索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是否存在,并且是否已完成。
然后会进行排队,排队之后,我们会再次进行上述检查。这两次检查,就是双重功能检查的意思。可以避免出现多个线程重复执行某段代码。假设没有第二次检查,则排队线程获得锁的时候,一样会有“通知用户”的行为。
表达能力有限,不知道上述内容有没有把我表达的意思说清楚。希望对看到本文的你有所帮助:-)。