参考链接:
http://blog.csdn.net/luoweifu/article/details/46613015
http://blog.csdn.net/lan861698789/article/details/50425554
简述
synchronized(this/obj){
//只要执行这段代码块的线程没执行完,对于this/obj对象的同步区域就是不允许无钥匙访问,如this或obj中的printCpu()方法
}
public synchronized void printCpu() {//同步块
}
理论
- 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
这可以理解为对内存区域上锁,static的就是所有对象的共享区,共享区上锁,所有对象对这个区域的访问都需要’钥匙’,普通对象享有自己的内存区域。上锁就只影响到对这个对象的操作
- 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
比如下面的Resource类,printCpu()方法是同步方法
public class Resource {
private int cpu;
public synchronized void printCpu() {
System.out.println(Thread.currentThread().getName()+":"+this.cpu++);
}
}
- 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。但是必要的同步操作还是需要的,避免造成数据的错乱。而且jdk也在对synchronized进行优化
总结
-
线程等待(阻塞):当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
-
非同步代码块可访问:当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
ps: 同步块和非同步块,就像一家商场里锁门的商店和没锁的商店,而不是说把商场锁了,线程就分为有钥匙和没钥匙的人,没钥匙的人就只能去访问没锁的商店
Tip:
当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:
class Test implements Runnable
{
private byte[] lock = new byte[0]; // 特殊的instance变量
public void method()
{
synchronized(lock) { //我理解为对lock上锁,且先运行到这里的线程拿了唯一一把钥匙
// todo 同步代码块
}
}
}
说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码: 生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
synchronized关键字不能继承:
class Parent {
public synchronized void method() { }
}
class Child extends Parent {
public synchronized void method() { }//方法还是需要使用关键字声明,或者无效
}
死锁demo:死锁是怎样形成的