backsurface->Lock()失败导致无法播放

backsurface->Lock()失败导致无法播放

编者:李国帅

qq:9611153 微信lgs9611153

时间:2009-12-11 04:58下午

问题相关:

有一个现象难以修改,(播放方案中存在多个播放器)一旦播放器在某个地方发生backsurface被锁定,所有的播放器全都调用backsurface->Lock()失败,这个播放方案没有一个播放器能够播放。

如果切换播放方案,播放器backsurface->Lock()成功。

所以问题一定发生在播放器内部,另外backsurface->Lock()具有同步特征。

 

使用的方式如下,原来我没有使用DDLOCK_WAIT标记,但是好像并不是唯一的原因。

锁定与解锁:

unsigned char* CDisplayHandle::LockBuffer(LPDIRECTDRAWSURFACE7 lpddsBack)

{

    ATLASSERT (lpddsBack != NULL);

    if(lpddsBack == NULL) return NULL;

 

    HRESULT ddrval;

    DDSURFACEDESC2 ddsd;

    ZeroMemory(&ddsd, sizeof(DDSURFACEDESC2));

    ddsd.dwSize = sizeof(DDSURFACEDESC2);

    //是不是应该使用DDLOCK_SURFACEMEMORYPTR代替 DDLOCK_WRITEONLY

    ddrval = lpddsBack->Lock(NULL, &ddsd, DDLOCK_NOSYSLOCK | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);

    if (FAILED(ddrval))//if (ddrval != DD_OK)

    {

        if (ddrval == DDERR_INVALIDPARAMS)

        {

            ddrval = lpddsBack->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL);

        }

        if (ddrval == DDERR_SURFACELOST)

        {

            lpddsBack->Restore();

            ddrval = lpddsBack->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL);

        }

    }

    //ATLASSERT(SUCCEEDED(ddrval));

    if (FAILED(ddrval))//if (ddrval != DD_OK)

    {

        CUtil::Instance()->DbgFilePrintf(L"TSClient",_T("TSPlayer: LockBuffer Backsurface Lock Failed. ddrval=0x%X."),ddrval);

        return NULL;

    }

 

    unsigned char* pBuffer = NULL;

    pBuffer = (unsigned char*)ddsd.lpSurface;

    ATLASSERT(pBuffer != NULL);

    if(pBuffer == NULL)lpddsBack->Unlock(NULL);

 

    return pBuffer;

}

 

void CDisplayHandle::UnlockBuffer(LPDIRECTDRAWSURFACE7 lpddsBack)

{

    ATLASSERT (lpddsBack != NULL);

    if(lpddsBack == NULL) return;

 

    HRESULT ddrval;

    ddrval = lpddsBack->Unlock(NULL);

    ATLASSERT(SUCCEEDED(ddrval));

    if (FAILED(ddrval))//if (ddrval != DD_OK)

    {

        CUtil::Instance()->DbgFilePrintf(L"TSClient",_T("TSPlayer: UnlockBuffer Backsurface Failed.ddrval=%d."),ddrval);

        return;

    }

    return;

}

 

 

 

 

 

<think>我们被要求将Java线程获取synchronized锁的过程(包括无锁、偏向锁、轻量级锁、自旋和重量级锁的转换)转换为流程图。我们将根据提供的引用(特别是引用[2]和引用[3])来构建这个流程。 步骤: 1. 首先,我们需要理解锁的状态转换过程。根据引用[3],锁的状态转换是无锁 → 偏向锁 → 轻量级锁 → 重量级锁。 2. 引用[2]详细描述了偏向锁的获取和升级情况,以及轻量级锁的获取和升级情况。 3. 我们还需要考虑自旋,虽然自旋不是一种锁状态,但它是轻量级锁在竞争时的一种优化策略(在升级为重量级锁之前可能会尝试自旋)。 根据引用[3]和引用[2],我们可以总结出以下步骤: - 当线程进入同步块时,首先检查锁对象的Mark Word。 - 判断锁标志位,根据不同的标志位进入不同的锁状态处理。 锁标志位(最后2位): 01:无锁或偏向锁(需要看前面的偏向锁标志) 00:轻量级锁 10:重量级锁 具体流程: 1. 初始状态:锁对象刚创建时,是无锁状态(01,且偏向锁标志为0)。 2. 当第一个线程访问同步块时,会尝试使用CAS将线程ID设置到Mark Word中,并将偏向锁标志置为1,进入偏向锁状态(101)。 3. 当同一个线程再次进入同步块(重入),因为线程ID是自己的,所以直接进入,无需任何操作。 4. 当另一个线程尝试获取锁时,发现偏向锁指向的是其他线程,那么会检查原来持有偏向锁的线程是否还存活或者已经退出同步代码块: a. 如果原持有线程已经退出同步块(即不再需要该锁),则撤销偏向锁,恢复到无锁状态(01),然后新线程通过CAS竞争偏向锁(将偏向锁指向自己)。 b. 如果原持有线程还在同步块中,则撤销偏向锁,升级为轻量级锁(00)。此时,原持有线程会继续执行,而新线程会在栈帧中创建锁记录(Lock Record),并通过CAS将锁对象的Mark Word替换为指向锁记录的指针。如果成功,则新线程获得轻量级锁。 5. 在轻量级锁状态下,如果有两个以上的线程竞争(即多个线程同时竞争同一个锁),或者同一个线程多次CAS失败(说明有竞争),那么轻量级锁会升级为重量级锁(10)。 在升级过程中,会进行自旋优化(在轻量级锁升级为重量级锁之前,线程可能会进行自旋等待,以避免直接进入重量级锁的阻塞状态)。自旋成功则获得锁,自旋失败则升级为重量级锁。 6. 重量级锁会使用操作系统的互斥量(Mutex)来实现,此时未获得锁的线程会被阻塞,进入等待队列(如EntryList、WaitSet等)。 注意:自旋发生在轻量级锁竞争失败后,升级为重量级锁之前。在重量级锁状态下,等待锁的线程不会自旋,而是被阻塞。 流程图如下(使用文字描述流程,然后我们可以用mermaid代码表示): 开始 -> 检查锁对象Mark Word 如果锁标志位是01(无锁或偏向锁): 检查偏向锁标志(第3位,注意:Mark Word的结构在不同位上有不同含义): 如果是0(无锁): 使用CAS尝试获取偏向锁(将线程ID写入Mark Word,并将偏向锁标志置1) 如果成功,则进入偏向锁状态(101) 否则,说明有竞争,进入轻量级锁获取流程(此时需要撤销偏向锁,升级为轻量级锁) 如果是1(偏向锁): 检查Mark Word中的线程ID是否当前线程: 如果是,则直接进入同步块(重入) 如果不是,则检查原持有偏向锁的线程是否还存活并且还在同步块中(通过遍历线程栈的锁记录): 如果原线程已经退出同步块,则撤销偏向锁(恢复到无锁状态),然后当前线程重新尝试获取偏向锁(CAS设置线程ID) 如果原线程还在同步块中,则撤销偏向锁,升级为轻量级锁(00),然后当前线程通过CAS竞争轻量级锁(将锁对象头替换为指向当前线程栈中锁记录的指针) 如果锁标志位是00(轻量级锁): 当前线程尝试使用CAS将锁对象头的Mark Word替换为指向自己栈帧中锁记录的指针 如果成功,则获取轻量级锁,进入同步块 如果失败,则说明有竞争(可能是多个线程竞争): 先进行自旋(循环尝试CAS)一定次数(自适应自旋): 如果自旋期间成功获得锁,则继续执行 如果自旋失败,则升级为重量级锁(10) 如果锁标志位是10(重量级锁): 线程进入阻塞状态,等待操作系统互斥量(Mutex)的唤醒 注意:在轻量级锁状态下,如果发生竞争(CAS失败),会先尝试自旋,自旋失败再升级为重量级锁。自旋的次数可以通过JVM参数调整,并且JDK6以后引入了自适应自旋。 另外,在锁升级过程中,锁对象的Mark Word会发生变化,同时会涉及锁撤销操作。 根据以上逻辑,我们可以绘制流程图(使用mermaid的flowchart TD语法)。 注意:由于自旋是在轻量级锁竞争失败后进行的,所以我们在轻量级锁获取失败后加入自旋步骤。 流程图代码:</think>### Java线程获取synchronized锁的完整流程图 ```mermaid flowchart TD Start([线程尝试进入synchronized块]) --> CheckMarkWord[检查对象头Mark Word] CheckMarkWord --> LockFlag01{锁标志位=01?} LockFlag01 -->|是| BiasedFlag{偏向锁标志=1?} LockFlag01 -->|否| LockFlag00{锁标志位=00?} LockFlag01 -->|否| LockFlag10{锁标志位=10?} %% === 偏向锁路径 === BiasedFlag -->|是| CheckThreadID{线程ID匹配?} BiasedFlag -->|否| SetupBias[设置偏向锁: 创建Lock Record存储Mark Word副本] SetupBias --> CAS_Bias[尝试CAS设置线程ID到Mark Word] CAS_Bias -->|成功| EnterBiased[进入偏向锁状态, 执行同步代码] CAS_Bias -->|失败| RevokeBias[撤销偏向锁] CheckThreadID -->|是| EnterBiased CheckThreadID -->|否| CheckRevoke[检查原持有线程状态] CheckRevoke -->|已退出| ReBias[尝试重新偏向: CAS设置新线程ID] CheckRevoke -->|仍活跃| UpgradeLight[升级为轻量级锁] %% === 轻量级锁路径 === LockFlag00 -->|是| CheckOwner[检查锁记录指针] CheckOwner -->|当前线程| Reenter[锁重入: 增加锁记录计数] CheckOwner -->|其他线程| SpinLock[开始自旋等待] SpinLock --> SpinSuccess{自旋成功?} SpinSpinSuccess -->|是| EnterLight[进入轻量级锁状态] SpinSuccess -->|否| UpgradeHeavy[升级为重量级锁] %% === 重量级锁路径 === LockFlag10 -->|是| EnterMonitor[进入重量级锁状态] EnterMonitor --> CheckEntry[检查Monitor的_EntryList] CheckEntry -->|空闲| AcquireMonitor[获取Monitor锁] CheckEntry -->|竞争| BlockThread[线程阻塞进入_WaitSet] %% === 无锁路径 === SetupBias -->|无锁状态| InitialLock[初始化锁] InitialLock -->|首次获取| SetBias[设置偏向锁] InitialLock -->|禁用偏向| SetupLight[直接设置轻量级锁] %% === 状态转换点 === RevokeBias --> UpgradeLight UpgradeLight --> SetupLight[创建Lock Record, CAS设置锁记录指针] SetupLight -->|成功| EnterLight SetupLight -->|失败| SpinLock EnterLight --> HeavyContention{发生竞争?} HeavyContention -->|是| UpgradeHeavy HeavyContention -->|否| SyncCode[执行同步代码] UpgradeHeavy --> CreateMonitor[创建Monitor对象] CreateMonitor --> SetHeavy[设置Mark Word指向Monitor] SetHeavy --> EnterMonitor %% === 最终状态 === EnterBiased --> SyncCode EnterLight --> SyncCode AcquireMonitor --> SyncCode SyncCode --> ExitLock[退出同步块] ExitLock -->|偏向锁| ClearBias[不清除线程ID, 保留偏向状态] ExitLock -->|轻量级锁| CAS_Reset[CAS恢复Mark Word] ExitLock -->|重量级锁| ReleaseMonitor[释放Monitor, 唤醒等待线程] ``` ### 流程详解与关键节点说明 #### 1. **初始检查阶段** - **锁标志位检测**:线程首先检查对象头Mark Word的锁标志位(最后2位)[^3]: - `01`:无锁或偏向锁 → 进入偏向锁判断 - `00`:轻量级锁 → 进入轻量级锁流程 - `10`:重量级锁 → 直接进入Monitor队列 - **偏向锁标志检测**:当标志位为`01`时,检查第3位偏向锁标志: - `1`:偏向锁生效 → 验证线程ID - `0`:无锁状态 → 尝试获取偏向锁 #### 2. **偏向锁处理** - **线程ID匹配**:检查Mark Word中的线程ID是否与当前线程一致[^2]: - 匹配:直接进入同步代码(锁重入) - 不匹配:检查原持有线程状态 - **偏向锁撤销**:当其他线程竞争时: - 原线程已退出:尝试CAS重偏向(新线程ID写入Mark Word) - 原线程仍活跃:升级为轻量级锁 #### 3. **轻量级锁处理** - **锁记录创建**:在栈帧创建Lock Record存储原Mark Word副本[^4] - **CAS竞争**:尝试将Mark Word替换为指向锁记录的指针: ```java // 伪代码实现 do { expected = markWord; // 当前Mark Word值 newLockRecord.address = expected; newValue = &lockRecord; // 指向锁记录的指针 } while (!CAS(markWord, expected, newValue)); ``` - **自旋优化**:CAS失败时自旋重试(默认次数10-50次)[^3]: - 成功:保持轻量级锁状态 - 失败:升级重量级锁 #### 4. **重量级锁处理** - **Monitor创建**:JVM创建ObjectMonitor对象[^3] - **线程阻塞**:竞争失败的线程进入_WaitSet队列 - **锁释放流程**: ```java void exitSync() { if (isHeavyLock) { monitor._owner = null; // 清除所有者 notify _EntryList; // 唤醒等待线程 } } ``` #### 5. **锁升级触发条件** | 当前状态 | 触发条件 | 升级目标 | |----------|----------|----------| | **偏向锁** | 两个以上线程竞争 | 轻量级锁 | | **轻量级锁** | 自旋失败或竞争加剧 | 重量级锁 | | **无锁** | 禁用偏向锁(-XX:-UseBiasedLocking) | 直接轻量级锁 | > 锁降级在synchronized中不会发生,这是与ReentrantLock的关键区别[^1] ### 性能优化建议 1. **偏向锁延迟**:通过`-XX:BiasedLockingStartupDelay=0`立即启用 2. **自旋调优**:设置最大自旋次数`-XX:PreBlockSpin=15` 3. **监控工具**: ```bash # 查看锁竞争情况 jstack <pid> | grep -A 10 "Monitor" ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

微澜-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值