synchronized
Java中提供synchronized关键字实现同步处理,用synchoronized可以修饰代码块,修饰方法,来完成对 对象加锁,对类加锁的操作。synchoronized是重量级锁。意思就是当有一个线程A进入时其他想访问的线程阻塞师等待,等待该线程A执行完整个加锁区间,系统自动释放锁,并唤醒等待的其他线程竞争锁。
对象锁
synchronized修饰同步代码块 --锁new出来的实例化对象synchoronized(this){}
代码举例子:
import .;
class MyRunnable implements Runnable{
private Integer ticket = 100;
@Override
public void run(){
for(int i=0;i<100;i++){
synchronized (this){
if(ticket>0){
(().getName()+"还剩"+(ticket--)+"张票");
}
}
}
}
}
public class Test{
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
synchronized修饰同步方法 --锁new出来的实例化对象public synchronized void func() {}
用synchronized修饰方法改写上述代码。
public synchronized void sale() {
if (ticket > 0) {
try {
(20);
} catch (InterruptedException e) {
();
}
(().getName() +"还剩" + (--) + "张票");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
synchronized锁对象称为对象锁,这种方式只是为了防止多个线程同时执行一个对象的同步段,而对象锁顾名思义,所得是对象,而不是括号中的代码。量相当于括号中的代码是临界资源互斥访问。
全局锁
synchoronized修饰类对象synchronized(类名.class){}
synchronized修饰类静态方法public static synchronized void func(){}
锁任意的实例化对象
如果我们在类内锁自己的实例化对象锁this,要要是在这个类类外呢,又或者锁包为我们提供的类呢?我们一样可以锁,直接锁该类的实例化对象。Object object = new Object();(obj){}
总结以下
synchronized可以修饰代码块和修饰方法。在两种修饰中又可以分别锁对象,锁类对象。对象锁是重量级锁。因为线程的阻塞以及唤醒都需要操作系统由用户态切换到内核态,开销大,效率低。
而且sunchronized修饰的锁有可重入性。也就是说一但一个A线程进入代码块或者方法a获取到了锁,BC等其他线程就需要等待。他就可以随意进入锁相同对象的其他方法b\c等中。
synchronized锁同步代码块底层实现
当我们使用synchronized代码块时,要执行同步代码块首先要执行系统moniterenter操作,尝试获取moniter对象。只有当线程获取到监视器moniter对象才可以执行同步代码块,否则只能阻塞等待。同一时刻只能有一个线程获取moniter对象。在执行完代码块退出时会执行moniterexit操作。因为退出时有很多可能性,可能正常退出,可能出现异常。所以往往一个moniterenter指令对应多条moniterexit指令,以保证无论异常还是正常退出都可以正确解锁。
synchronized锁同步方法底层实现
当用 synchronized 标记方法时,你会看到字节码中方法的访问标记为ACC_SYNCHRONIZED。该标记表示在进 入该方法时,Java 虚拟机需要进行 monitorenter 操作。而在退出该方法时,不管是正常返回,还是向调用者抛异 常,Java 虚拟机均需要进行 monitorexit 操作。
这里 monitorenter 和 monitorexit 操作所对应的锁对象是隐式的。对于实例方法来说,这两个操作对应的锁对象是 this;对于静态方法来说,这两个操作对应的锁对象则是所在类的 Class 实例。
1.当执行moniterenter时,会判断moniter计数器,如果值为0,表示此时锁没有被任何线程持有,此时会将锁的持有对象设置为当前线程,并且moniter+1操作。
2.当判断moniter计数器值不为0时,判断当前锁的持有对象是不是当前请求拿锁的线程,如果相同,则寄出去再次+1,体现了可重入性。如果不相同,则等待,直到持有锁的线程释放锁。
3.当执行moniterexit时,先将moniter计时器-1操作,如果计数器剪为0,则表示锁被当前持有对象释放,所以此时已经没有线程持有锁,唤醒所有等待线程去竞争锁。