synchronized关键字

本文详细解析了synchronized关键字的两种使用方式,包括其在对象实例和类级别的作用,以及通过ACC_SYNCHRONIZED标识和monitorenter/exit指令实现的锁机制。探讨了synchronized的重量级锁原理,包括等待队列、同步队列和锁计数器等概念。并通过实例代码展示了synchronized锁机制下线程的不可中断特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

synchronized关键字

synchronized 有两种加锁方式

(1) 某个对象实例内,防止多个线程对同一个实例对象的同时访问对该对象起作用

	//synchronized方法
	public synchronized void f() {
		//TODO
	}
	//synchronized代码块
	public void g() {
		synchronized(this){
		//TODO
		}
	}

synchronized方法所加的锁是通过ACC_SYNCHRONIZED 标识说明这是一个需要同步的方法。(此可通过Javap -c 命令反编译查看,下同)
synchronized所控制的代码块是通过指令monitorentry和monitorexit来获取锁和释放锁的。

每一个实例对象都是由对象头,实例数据,对齐填充组成

对象头由两部分组成:mark word以及类型指针(对象指向类元数据的指针,描述该实例是哪个类的实例)。其中Mark Word 中有一部分用来存储指向该实例的重量级锁的指针,并且有一个标志位来标识该实例是否被加锁。重量级锁是一个monitorObject对象,其中有等待队列(等待该锁的阻塞状态的线程),同步队列(某一线程释放该实例的锁之后等待队列里的阻塞线程移到此处),锁计数器(目前被重入次数+1,初值为0,代表还没有被线程所获取),目前锁的状态(当前哪个线程拥有该锁)。
当多个线程同时访问一段同步代码时,会先进入同步队列。jvm选择一个线程获取锁并运行,其他线程再次回到等待队列。

(2)某个类,防止多个线程同时访问同步代码块,可以对所有实例对象起作用

//synchronized类方法
public static synchronized void f(){
	//TODO
}
public void g(){
	synchronized(ClassName.class){
	//TODO
}

采用synchronized锁机制的线程在等待时不能被interrupt()打断

public class SynchronizedBlocked implements Runnable{

    public synchronized void f() {
        System.out.println("Trying to call f()");
        while(true) // Never releases lock
            Thread.yield();
    }

    /**
     * 在构造器中创建新线程并启动获取对象锁
     */
    public SynchronizedBlocked() {
        //该线程已持有当前实例锁
        new Thread() {
            public void run() {
                f(); // Lock acquired by this thread
            }
        }.start();
    }
    public void run() {
        //中断判断
        while (true) {
            if (Thread.interrupted()) {
                System.out.println("中断线程!!");
                break;
            } else {
            	System.out.println("-----");
                f();
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        SynchronizedBlocked sync = new SynchronizedBlocked();
        Thread t = new Thread(sync);
        //启动后调用f()方法,无法获取当前实例锁处于等待状态
        t.start();
        TimeUnit.SECONDS.sleep(1);
        //中断线程,无法生效
        t.interrupt();
    }
}
运行结果:
-----
Trying to call f()

上述代码分析:在SynchronizedBlocked类的构造器中,有一个线程匿名内部类。每当new一个SynchronizedBlocked类的实例时都会开启一个线程。该线程在调用方法f()。一直循环调用Thread.yield()方法,方法yield()会使当前线程回到线程池,jvm重新从线程池中选择要运行的线程(有可能还会运行该线程)。yield()方法并不会释放该线程所拥有的锁,以至于即使匿名线程给线程t让不了,但线程t永远都获取不到当前实例的锁,所以线程t一直处于阻塞状态。主线程在结束前调用了线程t的interrupt()方法,但线程t并没有被中断,可见interrupt()方法并不能中断被synchronized锁机制所阻塞的线程

synchronized关键字提供一种排它式同步机制,但有两个缺陷

(1)不能控制阻塞时长

线程一旦进入synchronized锁机制的阻塞状态,会一直等待,直到获取它所想要的锁。而且在阻塞的过程中,你并不知道要等待多久。

(2)阻塞不能被打断

线程在synchronized锁机制阻塞状态下,interrupt()方法并不能中断该线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值