synchronized关键字是利用锁的机制来实现同步的。锁机制具有互斥性(操作的原子性)和可见性。
synchronized可以修饰静态/非静态方法、代码块,可以对类或者对象加锁。
先来做个最简单的,用没用synchronized的对比:
import java.util.concurrent.TimeUnit;
public class SynchroDemo {
private static int i = 0;
private static int j = 0;
public synchronized void accessResources1() {
i++;
System.out.println("accessResources1,i=" + i + "," + Thread.currentThread().getName() + " is runing");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void accessResources2() {
j++;
System.out.println("accessResources2,j=" + j + "," + Thread.currentThread().getName() + " is runing");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
final SynchroDemo deno = new SynchroDemo();
for (int i = 0; i < 5; i++) {
Runnable runnable = new Runnable() {
public void run() {
deno.accessResources1();
deno.accessResources2();
}
};
Thread thread = new Thread(runnable);
thread.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
上面代码用JDK1.8运行的结果:
可以看出,用JDK1.8运行,进程拿到锁并非先到先得。(如果你电脑运行出来是五个线程特别乖巧逐个调用accessResources1的,那应该是你的电脑比我的更棒棒啰~可以把延时缩短一点试试)换个jdk1.5试试:
在JDK1.6前,Synchronized属于重量级锁,在JDK1.6,Synchronized得到了很多性能上的改进,引入了自旋锁、偏向锁、轻量级锁等技术来减少锁操作的开销,Synchronized锁也被优化成一个升级的过程。关于各种锁的特性,这里挖个坑,往后再整理记录。
与accessResources2相比,accessResources1方法有Synchronized的修饰,各个线程对变量i的修改有序递增,整个accessResources1方法满足了原子性、可见性和顺序性。注意,是整个accessResources1方法,这也是Synchronized的一个缺点:粒度太大,而且在压力太大的时候,若synchronized升级到了重量级锁,相当于是从并发退化到串行,会引起阻塞。
synchronized具有重入性,同一线程可以对同一对象进行多次加锁,不过要注意一个对象加锁后再对另一个对象加锁,不能交叉加锁,否则会造成死锁。
- 对象锁:
public synchronized void accessResources1() {
//锁定代码块的时候,所修饰的、要锁定的方法不能是静态方法
synchronized (this) {//this指的是当前对象
i++;
System.out.println("accessResources1,i=" + i + "," + Thread.currentThread().getName() + " is runing");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 类锁:
当类加载器把class文件加载到方法区的时候,向堆区生产了一个Class对象。由它产生的所有对象都对应了唯一的Class对象
public synchronized void accessResources1() {
//锁定代码块的时候,所修饰的、要锁定的方法不能是静态方法
synchronized (SynchroDemo.class) {
i++;
System.out.println("accessResources1,i=" + i + "," + Thread.currentThread().getName() + " is runing");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
想验证一下synchronized的互斥性,可以把延时时间拉长到一分钟,启用 Jconsole,通过线程堆栈分析: