溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么在Java中實(shí)現(xiàn)一個(gè)Lock接口

發(fā)布時(shí)間:2021-04-16 16:06:38 來源:億速云 閱讀:215 作者:Leah 欄目:編程語言

今天就跟大家聊聊有關(guān)怎么在Java中實(shí)現(xiàn)一個(gè)Lock接口,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

1、概述

JUC中l(wèi)ocks包下常用的類與接口圖如下:

怎么在Java中實(shí)現(xiàn)一個(gè)Lock接口

圖中,Lock和ReadWriteLock是頂層鎖的接口,Lock代表實(shí)現(xiàn)類是ReentrantLock(可重入鎖),ReadWriteLock(讀寫鎖)的代表實(shí)現(xiàn)類是ReentrantReadWriteLock。

ReadWriteLock 接口以類似方式定義了讀鎖而寫鎖。此包只提供了一個(gè)實(shí)現(xiàn),即 ReentrantReadWriteLock。

Condition 接口描述了可能會(huì)與鎖有關(guān)聯(lián)的條件變量。這些變量在用法上與使用 Object.wait 訪問的隱式監(jiān)視器類似,但提供了更強(qiáng)大的功能。需要特別指出的是,單個(gè) Lock 可能與多個(gè) Condition 對(duì)象關(guān)聯(lián)。

2、lock與synchronized比較

synchronized是java中的一個(gè)關(guān)鍵字,也就是說是Java語言內(nèi)置的特性。那么為什么會(huì)出現(xiàn)Lock呢?

1、Lock不是Java語言內(nèi)置的,synchronized是Java語言的關(guān)鍵字,因此是內(nèi)置特性。Lock是一個(gè)類,通過這個(gè)類可以實(shí)現(xiàn)同步訪問;

2、Lock和synchronized有一點(diǎn)非常大的不同,采用synchronized不需要用戶去手動(dòng)釋放鎖,當(dāng)synchronized方法或者synchronized代碼塊執(zhí)行完之后,系統(tǒng)會(huì)自動(dòng)讓線程釋放對(duì)鎖的占用;而Lock則必須要用戶去手動(dòng)釋放鎖,如果沒有主動(dòng)釋放鎖,就有可能導(dǎo)致出現(xiàn)死鎖現(xiàn)象。

synchronized 的局限性與Lock的優(yōu)點(diǎn) 

如果一個(gè)代碼塊被synchronized關(guān)鍵字修飾,當(dāng)一個(gè)線程獲取了對(duì)應(yīng)的鎖,并執(zhí)行該代碼塊時(shí),其他線程便只能一直等待直至占有鎖的線程釋放鎖。事實(shí)上,占有鎖的線程釋放鎖一般會(huì)是以下三種情況之一:

1:占有鎖的線程執(zhí)行完了該代碼塊,然后釋放對(duì)鎖的占有;

2:占有鎖線程執(zhí)行發(fā)生異常,此時(shí)JVM會(huì)讓線程自動(dòng)釋放鎖;

3:占有鎖線程進(jìn)入WAITING狀態(tài)從而釋放鎖,例如在該線程中調(diào)用wait()方法等。

下列三種情況: 

1 、在使用synchronized關(guān)鍵字的情形下,假如占有鎖的線程由于要等待IO或者其他原因(比如調(diào)用sleep方法)被阻塞了,但是又沒有釋放鎖,那么其他線程就只能一直等待,別無他法。這會(huì)極大影響程序執(zhí)行效率。因此,就需要有一種機(jī)制可以不讓等待的線程一直無期限地等待下去(比如只等待一定的時(shí)間 (解決方案:tryLock(long time, TimeUnit unit))或者能夠響應(yīng)中斷(解決方案:lockInterruptibly())),這種情況可以通過 Lock 解決。

2、當(dāng)多個(gè)線程讀寫文件時(shí),讀操作和寫操作會(huì)發(fā)生沖突現(xiàn)象,寫操作和寫操作也會(huì)發(fā)生沖突現(xiàn)象,但是讀操作和讀操作不會(huì)發(fā)生沖突現(xiàn)象。但是如果采用synchronized關(guān)鍵字實(shí)現(xiàn)同步的話,就會(huì)導(dǎo)致一個(gè)問題,即當(dāng)多個(gè)線程都只是進(jìn)行讀操作時(shí),也只有一個(gè)線程在可以進(jìn)行讀操作,其他線程只能等待鎖的釋放而無法進(jìn)行讀操作。因此,需要一種機(jī)制來使得當(dāng)多個(gè)線程都只是進(jìn)行讀操作時(shí),線程之間不會(huì)發(fā)生沖突。同樣地,Lock也可以解決這種情況 (解決方案:ReentrantReadWriteLock) 。

3、通過Lock得知線程有沒有成功獲取到鎖 (解決方案:ReentrantLock) ,但這個(gè)是synchronized無法辦到的。

上面提到的三種情形,我們都可以通過Lock來解決,但 synchronized 關(guān)鍵字卻無能為力。事實(shí)上,Lock 是 java.util.concurrent.locks包 下的接口,Lock 實(shí)現(xiàn)提供了比 synchronized 關(guān)鍵字更廣泛的鎖操作,它能以更優(yōu)雅的方式處理線程同步問題。也就是說,Lock提供了比synchronized更多的功能。

3、Lock接口實(shí)現(xiàn)類的使用

// 獲取鎖
void lock()
// 如果當(dāng)前線程未被中斷,則獲取鎖,可以響應(yīng)中斷
void lockInterruptibly()
// 返回綁定到此 Lock 實(shí)例的新 Condition 實(shí)例
Condition newCondition()
// 僅在調(diào)用時(shí)鎖為空閑狀態(tài)才獲取該鎖,可以響應(yīng)中斷
boolean tryLock()
// 如果鎖在給定的等待時(shí)間內(nèi)空閑,并且當(dāng)前線程未被中斷,則獲取鎖
boolean tryLock(long time, TimeUnit unit)
// 釋放鎖
void unlock()

3.1、在Lock中聲明了四個(gè)方法來獲取鎖,那么這四個(gè)方法有何區(qū)別呢?首先,lock()方法是平常使用得最多的一個(gè)方法,就是用來獲取鎖。如果鎖已被其他線程獲取,則進(jìn)行等待。在前面已經(jīng)講到,如果采用Lock,必須主動(dòng)去釋放鎖,并且在發(fā)生異常時(shí),不會(huì)自動(dòng)釋放鎖。因此,一般來說,使用Lock必須在try…catch…塊中進(jìn)行,并且將釋放鎖的操作放在finally塊中進(jìn)行,以保證鎖一定被被釋放,防止死鎖的發(fā)生。通常使用Lock來進(jìn)行同步的話,是以下面這種形式去使用的:

Lock lock = ...;
lock.lock();
try{
  //處理任務(wù)
}catch(Exception ex){
}finally{
  lock.unlock();  //釋放鎖
}

3.2、tryLock() & tryLock(long time, TimeUnit unit)

  tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true;如果獲取失敗(即鎖已被其他線程獲?。?,則返回false,也就是說,這個(gè)方法無論如何都會(huì)立即返回(在拿不到鎖時(shí)不會(huì)一直在那等待)。

  tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過區(qū)別在于這個(gè)方法在拿不到鎖時(shí)會(huì)等待一定的時(shí)間,在時(shí)間期限之內(nèi)如果還拿不到鎖,就返回false,同時(shí)可以響應(yīng)中斷。如果一開始拿到鎖或者在等待期間內(nèi)拿到了鎖,則返回true。

一般情況下,通過tryLock來獲取鎖時(shí)是這樣使用的:

Lock lock = ...;
if(lock.tryLock()) {
   try{
     //處理任務(wù)
   }catch(Exception ex){
   }finally{
     lock.unlock();  //釋放鎖
   }
}else {
  //如果不能獲取鎖,則直接做其他事情
}

3.3、lockInterruptibly() 
  lockInterruptibly()方法比較特殊,當(dāng)通過這個(gè)方法去獲取鎖時(shí),如果線程 正在等待獲取鎖,則這個(gè)線程能夠響應(yīng)中斷,即中斷線程的等待狀態(tài)。例如,當(dāng)兩個(gè)線程同時(shí)通過lock.lockInterruptibly()想獲取某個(gè)鎖時(shí),假若此時(shí)線程A獲取到了鎖,而線程B只有在等待,那么對(duì)線程B調(diào)用threadB.interrupt()方法能夠中斷線程B的等待過程。

  由于lockInterruptibly()的聲明中拋出了異常,所以lock.lockInterruptibly()必須放在try塊中或者在調(diào)用lockInterruptibly()的方法外聲明拋出 InterruptedException,但推薦使用后者,原因稍后闡述。因此,lockInterruptibly()一般的使用形式如下:

public void method() throws InterruptedException {
  lock.lockInterruptibly();
  try { 
   //.....
  }
  finally {
    lock.unlock();
  } 
}

注意,當(dāng)一個(gè)線程獲取了鎖之后,是不會(huì)被interrupt()方法中斷的。因?yàn)閕nterrupt()方法只能中斷阻塞過程中的線程而不能中斷正在運(yùn)行過程中的線程。因此,當(dāng)通過lockInterruptibly()方法獲取某個(gè)鎖時(shí),如果不能獲取到,那么只有進(jìn)行等待的情況下,才可以響應(yīng)中斷的。與 synchronized 相比,當(dāng)一個(gè)線程處于等待某個(gè)鎖的狀態(tài),是無法被中斷的,只有一直等待下去。
范例,運(yùn)行起來后,Thread2能夠被正確中斷。

public class Test {
  private Lock lock = new ReentrantLock(); 
  public static void main(String[] args) {
    Test test = new Test();
    MyThread thread1 = new MyThread(test);
    MyThread thread2 = new MyThread(test);
    thread1.start();
    thread2.start();
     
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    thread2.interrupt();
  } 
   
  public void insert(Thread thread) throws InterruptedException{
    lock.lockInterruptibly();  //注意,如果需要正確中斷等待鎖的線程,必須將獲取鎖放在外面,然后將InterruptedException拋出
    try { 
      System.out.println(thread.getName()+"得到了鎖");
      long startTime = System.currentTimeMillis();
      for(  ;   ;) {
        if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
          break;
        //插入數(shù)據(jù)
      }
    }
    finally {
      System.out.println(Thread.currentThread().getName()+"執(zhí)行finally");
      lock.unlock();
      System.out.println(thread.getName()+"釋放了鎖");
    } 
  }
}
class MyThread extends Thread {
  private Test test = null;
  public MyThread(Test test) {
    this.test = test;
  }
  @Override
  public void run() {
     
    try {
      test.insert(Thread.currentThread());
    } catch (InterruptedException e) {
      System.out.println(Thread.currentThread().getName()+"被中斷");
    }
  }
}

3.4 具體的鎖實(shí)現(xiàn)

Lock的實(shí)現(xiàn)類

ReentrantLock :即 可重入鎖。ReentrantLock是唯一實(shí)現(xiàn)了Lock接口的類,并且ReentrantLock提供了更多的方法。

ReadWriteLock鎖:接口只有兩個(gè)方法:

//返回用于讀取操作的鎖
Lock readLock()
//返回用于寫入操作的鎖
Lock writeLock()

ReadWriteLock維護(hù)了一對(duì)相關(guān)的鎖,一個(gè)用于只讀操作,另一個(gè)用于寫入操作。范例

class ReadWriteLockQueue {
  //共享數(shù)據(jù),只能有一個(gè)線程 寫數(shù)據(jù),但可以多個(gè)線程讀數(shù)據(jù)
  private Object data = null;
  private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
  //讀數(shù)據(jù)
  public void get() {
    try {
      rwl.readLock().lock();//上讀鎖,其他線程只能讀。
      System.out.print(Thread.currentThread().getName() + "讀取 data!");
      Thread.sleep((long) (Math.random() * 1000));
        System.out.println(Thread.currentThread().getName() + "讀取到的數(shù)據(jù):"+ data);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      rwl.readLock().unlock();//釋放讀鎖
    }
  }
  //寫數(shù)據(jù)
  public void put(Object data) {
    try {
      rwl.writeLock().lock();//加上寫鎖,不允許其他線程 讀寫
      System.out.print(Thread.currentThread().getName() + "寫入數(shù)據(jù),");
      Thread.sleep((long) (Math.random() * 1000));
      this.data = data;
      System.out.println(Thread.currentThread().getName() + "已經(jīng)寫好數(shù)據(jù)" + data);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      rwl.writeLock().unlock();//釋放鎖
    }
  }
}
public class TestReentrantReadWriteLock {
  public static void main(String[] args) {
    final ReadWriteLockQueue readWriteLockQueue = new ReadWriteLockQueue();
    for (int i = 0; i < 2 ; i++) {
      new Thread(new Runnable() {
        @Override
        public void run() {
          while (true) {
            readWriteLockQueue.put(new Random().nextInt(10000));
          }
        }
      },"寫線程").start();
      new Thread(new Runnable() {
        @Override
        public void run() {
          while(true) {
            readWriteLockQueue.get();
          }
        }
      },"讀線程").start();
    }
  }
}

4、鎖的相關(guān)概念

  • 可重入鎖 : 如果鎖具備可重入性,則稱作為 可重入鎖 。像 synchronized和ReentrantLock都是可重入鎖,可重入性在我看來實(shí)際上表明了 鎖的分配機(jī)制:基于線程的分配,而不是基于方法調(diào)用的分配。舉個(gè)簡單的例子,當(dāng)一個(gè)線程執(zhí)行到某個(gè)synchronized方法時(shí),比如說method1,而在method1中會(huì)調(diào)用另外一個(gè)synchronized方法method2,此時(shí)線程不必重新去申請(qǐng)鎖,而是可以直接執(zhí)行方法method2。

  • 可中斷鎖:顧名思義,可中斷鎖就是可以響應(yīng)中斷的鎖。在Java中,synchronized就不是可中斷鎖,而Lock是可中斷鎖。如果某一線程A正在執(zhí)行鎖中的代碼,另一線程B正在等待獲取該鎖,可能由于等待時(shí)間過長,線程B不想等待了,想先處理其他事情,我們可以讓它中斷自己或者在別的線程中中斷它,這種就是可中斷鎖。在前面演示tryLock(long time, TimeUnit unit)和lockInterruptibly()的用法時(shí)已經(jīng)體現(xiàn)了Lock的可中斷性。

  • 公平鎖:公平鎖即盡量以請(qǐng)求鎖的順序來獲取鎖。比如,同是有多個(gè)線程在等待一個(gè)鎖,當(dāng)這個(gè)鎖被釋放時(shí),等待時(shí)間最久的線程(最先請(qǐng)求的線程)會(huì)獲得該所,這種就是公平鎖。而非公平鎖則無法保證鎖的獲取是按照請(qǐng)求鎖的順序進(jìn)行的,這樣就可能導(dǎo)致某個(gè)或者一些線程永遠(yuǎn)獲取不到鎖。在Java中,synchronized就是非公平鎖,它無法保證等待的線程獲取鎖的順序。而對(duì)于ReentrantLock 和 ReentrantReadWriteLock,它默認(rèn)情況下是非公平鎖,但是可以設(shè)置為公平鎖。

  • 樂觀鎖:總是假設(shè)最好的情況,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒有去更新這個(gè)數(shù)據(jù),可以使用版本號(hào)機(jī)制和CAS算法實(shí)現(xiàn)。樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量,像數(shù)據(jù)庫提供的類似于write_condition機(jī)制,其實(shí)都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實(shí)現(xiàn)方式CAS實(shí)現(xiàn)的。

鎖主要存在四中狀態(tài),依次是:無鎖狀態(tài)、偏向鎖狀態(tài)、輕量級(jí)鎖狀態(tài)、重量級(jí)鎖狀態(tài),他們會(huì)隨著競(jìng)爭的激烈而逐漸升級(jí)。注意鎖可以升級(jí)不可降級(jí),這種策略是為了提高獲得鎖和釋放鎖的效率。

4.1、偏向鎖

引入偏向鎖的目的和引入輕量級(jí)鎖的目的很像,他們都是為了沒有多線程競(jìng)爭的前提下,減少傳統(tǒng)的重量級(jí)鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗。但是不同是:輕量級(jí)鎖在無競(jìng)爭的情況下使用 CAS (Compare and Swap)操作去代替使用互斥量。而偏向鎖在無競(jìng)爭的情況下會(huì)把整個(gè)同步都消除掉。

偏向鎖的“偏”就是偏心的偏,它的意思是會(huì)偏向于第一個(gè)獲得它的線程,如果在接下來的執(zhí)行中,該鎖沒有被其他線程獲取,那么持有偏向鎖的線程就不需要進(jìn)行同步!關(guān)于偏向鎖的原理可以查看《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐》第二版的13章第三節(jié)鎖優(yōu)化。

但是對(duì)于鎖競(jìng)爭比較激烈的場(chǎng)合,偏向鎖就失效了,因?yàn)檫@樣場(chǎng)合極有可能每次申請(qǐng)鎖的線程都是不相同的,因此這種場(chǎng)合下不應(yīng)該使用偏向鎖,否則會(huì)得不償失,需要注意的是,偏向鎖失敗后,并不會(huì)立即膨脹為重量級(jí)鎖,而是先升級(jí)為輕量級(jí)鎖。

4.2、 輕量級(jí)鎖

倘若偏向鎖失敗,虛擬機(jī)并不會(huì)立即升級(jí)為重量級(jí)鎖,它還會(huì)嘗試使用一種稱為輕量級(jí)鎖的優(yōu)化手段(1.6之后加入的)。輕量級(jí)鎖不是為了代替重量級(jí)鎖,它的本意是在沒有多線程競(jìng)爭的前提下,減少傳統(tǒng)的重量級(jí)鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗,因?yàn)槭褂幂p量級(jí)鎖時(shí),不需要申請(qǐng)互斥量。另外,輕量級(jí)鎖的加鎖和解鎖都用到了CAS操作。 關(guān)于輕量級(jí)鎖的加鎖和解鎖的原理可以查看

《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐》第二版的13章第三節(jié)鎖優(yōu)化。
輕量級(jí)鎖能夠提升程序同步性能的依據(jù)是“對(duì)于絕大部分鎖,在整個(gè)同步周期內(nèi)都是不存在競(jìng)爭的”,這是一個(gè)經(jīng)驗(yàn)數(shù)據(jù)。如果沒有競(jìng)爭,輕量級(jí)鎖使用 CAS 操作避免了使用互斥操作的開銷。但如果存在鎖競(jìng)爭,除了互斥量開銷外,還會(huì)額外發(fā)生CAS操作,因此在有鎖競(jìng)爭的情況下,輕量級(jí)鎖比傳統(tǒng)的重量級(jí)鎖更慢!如果鎖競(jìng)爭激烈,那么輕量級(jí)將很快膨脹為重量級(jí)鎖!

4.3、自旋鎖和自適應(yīng)自旋鎖

輕量級(jí)鎖失敗后,虛擬機(jī)為了避免線程真實(shí)地在操作系統(tǒng)層面掛起,還會(huì)進(jìn)行一項(xiàng)稱為自旋鎖的優(yōu)化手段。
互斥同步對(duì)性能最大的影響就是阻塞的實(shí)現(xiàn),因?yàn)閽炱鹁€程/恢復(fù)線程的操作都需要轉(zhuǎn)入內(nèi)核態(tài)中完成(用戶態(tài)轉(zhuǎn)換到內(nèi)核態(tài)會(huì)耗費(fèi)時(shí)間)。

一般線程持有鎖的時(shí)間都不是太長,所以僅僅為了這一點(diǎn)時(shí)間去掛起線程/恢復(fù)線程是得不償失的。 所以,虛擬機(jī)的開發(fā)團(tuán)隊(duì)就這樣去考慮:“我們能不能讓后面來的請(qǐng)求獲取鎖的線程等待一會(huì)而不被掛起呢?看看持有鎖的線程是否很快就會(huì)釋放鎖”。為了讓一個(gè)線程等待,我們只需要讓線程執(zhí)行一個(gè)忙循環(huán)(自旋),這項(xiàng)技術(shù)就叫做自旋。
何謂自旋鎖?它是為實(shí)現(xiàn)保護(hù)共享資源而提出一種鎖機(jī)制。其實(shí),自旋鎖與互斥鎖比較類似,它們都是為了解決對(duì)某項(xiàng)資源的互斥使用。無論是互斥鎖,還是自旋鎖,在任何時(shí)刻,最多只能有一個(gè)保持者,也就說,在任何時(shí)刻最多只能有一個(gè)執(zhí)行單元獲得鎖。但是兩者在調(diào)度機(jī)制上略有不同。對(duì)于互斥鎖,如果資源已經(jīng)被占用,資源申請(qǐng)者只能進(jìn)入睡眠狀態(tài)。但是自旋鎖不會(huì)引起調(diào)用者睡眠,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持,調(diào)用者就一直循環(huán)在那里看是否該自旋鎖的保持者已經(jīng)釋放了鎖,"自旋"一詞就是因此而得名。

JDK1.6及1.6之后,自旋鎖就改為默認(rèn)開啟的了。需要注意的是:自旋等待不能完全替代阻塞,因?yàn)樗€是要占用處理器時(shí)間。如果鎖被占用的時(shí)間短,那么效果當(dāng)然就很好了!反之,相反!自旋等待的時(shí)間必須要有限度。如果自旋超過了限定次數(shù)任然沒有獲得鎖,就應(yīng)該掛起線程。自旋次數(shù)的默認(rèn)值是10次,但是用戶可以修改。
在 JDK1.6 中引入了自適應(yīng)的自旋鎖。自適應(yīng)的自旋鎖帶來的改進(jìn)就是:自旋的時(shí)間不在固定了,而是和前一次同一個(gè)鎖上的自旋時(shí)間以及鎖的擁有者的狀態(tài)來決定,虛擬機(jī)變得越來越“聰明”了。

4.4、鎖消除

鎖消除理解起來很簡單,它指的就是虛擬機(jī)即使編譯器在運(yùn)行時(shí),如果檢測(cè)到那些共享數(shù)據(jù)不可能存在競(jìng)爭,那么就執(zhí)行鎖消除。鎖消除可以節(jié)省毫無意義的請(qǐng)求鎖的時(shí)間。

4.5、鎖粗化

原則上在編寫代碼的時(shí)候,總是推薦將同步快的作用范圍限制得盡量小——只在共享數(shù)據(jù)的實(shí)際作用域才進(jìn)行同步,這樣是為了使得需要同步的操作數(shù)量盡可能變小,如果存在鎖競(jìng)爭,那等待線程也能盡快拿到鎖。

看完上述內(nèi)容,你們對(duì)怎么在Java中實(shí)現(xiàn)一個(gè)Lock接口有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI