AQS設計思想與重要欄位詳解

2021-01-10 探險家之指路明燈

本文基於JDK1.8

本篇學習目標

了解AQS的設計思想以及重要欄位含義,如通過state欄位表示同步狀態等。

了解AQS內部維護鏈式雙向同步隊列的結構以及幾個重要指針。

了解五種重要的同步狀態。

明確兩種模式:共享模式和獨佔模式。

學習兩種模式下AQS提供的模板方法:獲取與釋放同步狀態相關方法。

了解Condition、ConditionObject等AQS對條件變量的支持。

通過Condition的案例深入了解等待通知的機制。

AQS概述

AQS即AbstractQueuedSynchronizer,隊列同步器,他是構建眾多同步組件的基礎框架,如ReentrantLock、ReentrantReadWriteLock等,是J.U.C並發包的核心基礎組件。

AQS框架基於模板方法設計模式構建,子類通過繼承它,實現它提供的抽象方法來管理同步狀態。

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { // 序列化版本號 private static final long serialVersionUID = 7373984972572414691L; /** * 創建AQS實例,初始化state為0,為子類提供 */ protected AbstractQueuedSynchronizer() { } /*- 同步隊列構成 */ // 等待隊列節點類型 static final class Node { //...省略 } /** * 除了初始化之外,它只能通過setHead方法進行修改。注意:如果head存在,它的waitStatus保證不會被取消 */ private transient volatile Node head; /** * 等待隊列的尾部,懶初始化,之後只在enq方法加入新節點時修改 */ private transient volatile Node tail; /*- 同步狀態相關 */ /** * volatile修飾, 標識同步狀態,state為0表示鎖空閒,state>0表示鎖被持有,可以大於1,表示被重入 */ private volatile int state; /** * 返回當前同步狀態 */ protected final int getState() { return state; } /** * 設置同步狀態 */ protected final void setState(int newState) { state = newState; } /** * 利用CAS操作更新state值 */ protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } static final long spinForTimeoutThreshold = 1000L; // 這部分和CAS有關 // 獲取Unsafe實例 private static final Unsafe unsafe = Unsafe.getUnsafe(); // 記錄state在AQS類中的偏移值 private static final long stateOffset; static { try { // 初始化state變量的偏移值 stateOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("state")); } catch (Exception ex) { throw new Error(ex); } }}以上包括AQS的基本欄位,比較核心的就是兩個部分:

內部FIFO隊列的節點類型Node,和首尾指針欄位。

同步狀態相關的方法,設置,獲取,CAS更新等。

接下來我們將一一學習這些內容。

AbstractOwnableSynchronizer

AbstractQueuedSynchronizer繼承自AbstractOwnableSynchronizer,它提供了設置或獲取獨佔鎖的擁有者線程的功能。

public abstract class AbstractOwnableSynchronizer implements java.io.Serializable { private static final long serialVersionUID = 3737899427754241961L; // 本身是抽象類,該構造方法實為為子類提供 protected AbstractOwnableSynchronizer() { } /* 互斥模式同步下的當前線程 */ private transient Thread exclusiveOwnerThread; /* 設置當前擁有獨佔訪問的線程。鎖的擁有線程,null 參數表示沒有線程擁有訪問。 * 此方法不另外施加任何同步或 volatile 欄位訪問。 */ protected final void setExclusiveOwnerThread(Thread t) { exclusiveOwnerThread = t; } /* 返回由 setExclusiveOwnerThread 最後設置的線程; * 如果從未設置,則返回 null。 * 此方法不另外施加任何同步或 volatile 欄位訪問。 */ protected final Thread getExclusiveOwnerThread() { return exclusiveOwnerThread; }}這裡exclusiveOwnerThread欄位用來判斷當前線程是不是持有鎖,因為鎖可以重入嘛,因此就會產生下面這樣的偽代碼:

if (currThread == getExclusiveOwnerThread()) { state++;}同步隊列與Node節點

tips:同步隊列被稱為CLH隊列,是Craig,Landin,Hagersten的合稱。

AQS通過內置的FIFO同步雙向隊列來完成資源獲取線程的排隊工作,內部通過節點head【實際上是虛擬節點,真正的第一個線程在head.next的位置】和tail記錄隊首和隊尾元素,隊列元素類型為Node。

CLU同步隊列的結構如下,具體的操作之後再做總結:

圖源:【AQS】核心實現

如果當前線程獲取同步狀態失敗(鎖)時,AQS 則會將當前線程以及等待狀態等信息構造成一個節點(Node)並將其加入同步隊列,同時會阻塞當前線程

當同步狀態釋放時,則會把節點中的線程喚醒,使其再次嘗試獲取同步狀態。

/** * 等待隊列中的節點類 */ static final class Node { /** 標識共享式節點 */ static final Node SHARED = new Node(); /** 標識獨佔式節點 */ static final Node EXCLUSIVE = null; /** -- 等待狀態 */ /** 表示該線程放棄對鎖的爭奪 */ static final int CANCELLED = 1; /** 當前node的後繼節點對應的線程需要被喚醒 */ static final int SIGNAL = -1; /** 線程在條件隊列中等待 */ static final int CONDITION = -2; /** 釋放共享資源時需要通知其他節點 */ static final int PROPAGATE = -3; /** waitStatus == 0 表示不是以上任何一種 */ // 記錄當前線程的等待狀態,以上五種 volatile int waitStatus; // 前驅節點 volatile Node prev; // 後繼節點 volatile Node next; // node存儲的線程 volatile Thread thread; // 當前節點在Condition中等待隊列上的下一個節點 Node nextWaiter; // 判斷是否為共享式獲取同步狀態 final boolean isShared() { return nextWaiter == SHARED; } /** * 為什麼不直接判斷prev,而是用p變量判斷呢? * 避免並發的情況下,prev判斷完為null,恰好被修改 */ final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } // 用於SHARED的創建 Node() { } // 用於addWaiter(Node mode)方法,指定模式的 Node(Thread thread, Node mode) { this.nextWaiter = mode; this.thread = thread; } // 用於addConditionWaiter()方法 Node(Thread thread, int waitStatus) { this.waitStatus = waitStatus; this.thread = thread; } }注釋中已經標註各個等待狀態的取值和表示,這邊再總結一下:

等待狀態使用waitStatus欄位表示,用來控制線程的阻塞和喚醒,除了上面寫的四種,實際還有一種狀態是0,這在源碼的注釋中已經明確。

【CANCELLED = 1】取消狀態,由於超時或中斷,節點會被設置為取消狀態,且一直保持,不會參與到競爭中。如果waitStatus>0則可以認為是該線程取消了等待。

【SIGNAL = -1】後繼節點的線程處於等待狀態,當前節點的線程如果釋放了同步狀態或被取消,將通知後繼節點,使後繼節點的線程得以運行。

【CONDITION = -2】節點在等待隊列中,節點線程等待在Condition上,當其他線程對Condition調用了signal()後,該節點將會從等待隊列中轉移到同步隊列中,加入到同步狀態的獲取中。

【PROPAGATE = -3】表示下一次共享式同步狀態獲取,將會無條件地傳播下去。

0:初始狀態,上面幾種啥也不是。

關於AQS內部維護的同步隊列,這裡只是了解一些基本概念,後續對隊列操作注意點進行深入學習。

同步狀態state

AQS使用一個int類型的成員變量state來表示同步狀態,它用volatile修飾,並且提供了關於state的三個線程安全的方法:

getState(),讀取同步狀態。

setState(int newState),設置同步狀態為newState。

compareAndSetState(int expect, int update),CAS操作更新state值。

state為0表示鎖空閒,state>0表示鎖被持有,可以大於1,表示被重入。不同的子類實現,對state的含義表示略有差異,舉幾個例子吧:

ReentrantLock:state表示當前線程獲取鎖的可重入次數。

ReetrantReadWriteLock:state的高16位表示讀狀態,也就是獲取該讀鎖的次數,低16位表示獲取到寫鎖的線程的可重入次數。

semaphore:state表示當前可用信號的個數。

CountDownlatch:state表示計數器當前的值。

重要方法分析

對於AQS來說,線程同步的關鍵是對state進行操作,根據state是否屬於一個線程,操作state的方式分為獨佔方式和共享方式。

獨佔式獲取與釋放同步狀態

使用獨佔的方式獲取的資源是與具體線程綁定的,如果一個線程獲取到了資源,便標記這個線程已經獲取到,其他線程再次嘗試操作state獲取資源時就會發現當前該資源不是自己持有的,就會在獲取失敗後阻塞。

比如獨佔鎖ReentrantLock的實現,當一個線程獲取了ReentrantLock的鎖後,在AQS內部會首先使用CAS操作把state狀態值從0變為1,然後設置當前鎖的持有者為當前線程,當該線程再次獲取鎖時發現它就是鎖的持有者,則會把狀態值從1變為2,也就是設置可重入次數,而當另外一個線程獲取鎖時發現自己並不是該鎖的持有者就會被放入AQS阻塞隊列後掛起。

// 獨佔式獲取同步狀態,成功後,其他線程需要等待該線程釋放同步狀態才能獲取同步狀態 public final void acquire(int arg) { // 首先調用 tryAcquire【需要子類實現】嘗試獲取資源,本質就是設置state的值,獲取成功就直接返回 if (!tryAcquire(arg) && // 獲取失敗,就將當前線程封裝成類型為Node.EXCLUSIVE的Node節點,並插入AQS阻塞隊列尾部 // 然後通過自旋獲取同步狀態 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } // 與 acquire(int arg) 相同,但是該方法響應中斷。 // 如果其他線程調用了當前線程的interrupt()方法,響應中斷,拋出異常。 public final void acquireInterruptibly(int arg) throws InterruptedException { // interrupted()方法將會獲取當前線程的中斷標誌並重置 if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); } //嘗試獲取鎖,如果獲取失敗會將當前線程掛起指定時間,時間到了之後當前線程被激活,如果還是沒有獲取到鎖,就返回false。 //另外,該方法會對中斷進行的響應,如果其他線程調用了當前線程的interrupt()方法,響應中斷,拋出異常。 public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout); } // 獨佔式釋放同步狀態 public final boolean release(int arg) { // 嘗試使用tryRelease釋放資源,本質也是設置state的值 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) // LockSupport.unpark(thread) 激活AQS裡面被阻塞的一個線程 // 被激活的線程則使用tryAcquire 嘗試,看當前狀態變量state的值是否能滿足自己的需要, //滿足則該線程被激活,然後繼續向下運行,否則還是會被放入AQS隊列並被掛起。 unparkSuccessor(h); return true; } return false; }需要注意:tryRelease和tryAcquire方法並沒有在AQS中給出實現,實現的任務交給了具體的子類,子類根據具體的場景需求實現,通過CAS算法,設置修改state的值。

共享式獲取與釋放同步狀態

對應共享方式的資源與具體線程是不相關的,當多個線程去請求資源時通過CAS 方式競爭獲取資源,當一個線程獲取到了資源後,另外一個線程再次去獲取時如果當前資源還能滿足它的需要,則當前線程只需要使用CAS 方式進行獲取即可。

比如Semaphore信號量,當一個線程通過acquire()方法獲取信號量時,會首先看當前信號量個數是否滿足需要,不滿足則把當前線程放入阻塞隊列,如果滿足則通過自旋CAS獲取信號量。

//共享式獲取同步狀態,如果當前線程未獲取到同步狀態,將會進入同步隊列等待, // 與獨佔式的主要區別是在同一時刻可以有多個線程獲取到同步狀態; public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) // 嘗試獲取資源,如果成功則直接返回 // 如果失敗,則將當前線程封裝為類型為Node.SHARED的Node節點並插入AQS阻塞隊列尾部 // 並使用LockSupport.park(this)掛起自己 doAcquireShared(arg); } // 共享式獲取同步狀態,響應中斷 public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); } //共享式獲取同步狀態,增加超時限制 public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); return tryAcquireShared(arg) >= 0 || doAcquireSharedNanos(arg, nanosTimeout); } //共享式釋放同步狀態 public final boolean releaseShared(int arg) { // 嘗試釋放資源 if (tryReleaseShared(arg)) { // 調用LockSupport.unpark(thread)激活AQS隊列裡被阻塞的一個線程。 // 被激活的線程使用tryReleaseShared查看當前狀態變量state是否能滿足自己的需要。 // 如果滿足需要,則線程被激活繼續向下運行,否則還是放入AQS隊列並被掛起 doReleaseShared(); return true; } return false; }Interruptibly的方法表示對中斷需要進行響應,線程在調用帶Interruptibly關鍵字的方法獲取資源時或者獲取資源失敗被掛起,其他線程中斷了該線程,那麼該線程會拋出InterruptedException異常而返回。

AQS條件變量的支持

Condition接口

Contition是一種廣義上的條件隊列,它利用await()和signal()為線程提供了一種更為靈活的等待/通知模式。

Condition必須要配合Lock一起使用,因為對共享狀態變量的訪問發生在多線程環境下。一個Condition的實例必須與一個Lock綁定,因此await和signal的調用必須在lock和unlock之間,有鎖之後,才能使用condition嘛。以ReentrantLock為例,簡單使用如下:

public class ConditionTest { public static void main(String[] args) { final ReentrantLock lock = new ReentrantLock(); final Condition condition = lock.newCondition(); Thread thread1 = new Thread(() -> { String name = Thread.currentThread().getName(); lock.lock(); System.out.println(name + " <==成功獲取到鎖" + lock); try { System.out.println(name + " <==進入條件隊列等待"); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name + " <==醒了"); lock.unlock(); System.out.println(name + " <==釋放鎖"); }, "等待線程"); thread1.start(); Thread thread2 = new Thread(() -> { String name = Thread.currentThread().getName(); lock.lock(); System.out.println(name + " ==>成功獲取到鎖" + lock); try { System.out.println("========== 這裡演示await中的線程沒有被signal的時候會一直等著 ==========="); Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name + " ==>通知等待隊列的線程"); condition.signal(); lock.unlock(); System.out.println(name + " ==>釋放鎖"); }, "通知線程"); thread2.start(); }}等待線程 <==成功獲取到鎖java.util.concurrent.locks.ReentrantLock@3642cea8[Locked by thread 等待線程]等待線程 <==進入條件隊列等待通知線程 ==>成功獲取到鎖java.util.concurrent.locks.ReentrantLock@3642cea8[Locked by thread 通知線程]========== 這裡演示await中的線程沒有被signal的時候會一直等著 ===========通知線程 ==>通知等待隊列的線程通知線程 ==>釋放鎖等待線程 <==醒了等待線程 <==釋放鎖ConditionObject內部類

AQS,Lock,Condition,ConditionObject之間的關係:

ConditionObject是AQS的內部類,實現了Condition接口,Lock中提供newCondition()方法,委託給內部AQS的實現Sync來創建ConditionObject對象,享受AQS對Condition的支持。

// ReentrantLock#newCondition public Condition newCondition() { return sync.newCondition(); } // Sync#newCondition final ConditionObject newCondition() { // 返回Contition的實現,定義在AQS中 return new ConditionObject(); }ConditionObject用來結合鎖實現線程同步,ConditionObject可以直接訪問AQS對象內部的變量,比如state狀態值和AQS隊列。ConditionObject是條件變量,每個條件變量對應一個條件隊列(單向鍊表隊列),其用來存放調用條件變量的await方法後被阻塞的線程,ConditionObject維護了首尾節點:

public class ConditionObject implements Condition, java.io.Serializable { private static final long serialVersionUID = 1173984872572414699L; /** First node of condition queue. */ private transient Node firstWaiter; /** Last node of condition queue. */ private transient Node lastWaiter;}看到這裡我們需要明確這裡的條件隊列和上面分析的同步隊列是不一樣的:

AQS維護的是當前在等待資源的隊列,Condition維護的是在等待signal信號的隊列。

每個線程會存在上述兩個隊列中的一個,lock與unlock對應在AQS隊列,signal與await對應條件隊列,線程節點在他們之間反覆橫跳。

這裡我們針對上面的demo來分析一下會更好理解一些:

為了簡化,接下來我將用D表示等待線程,用T表示通知線程。

【D】先調用lock.lock()方法,此時無競爭,【D】被加入到AQS等待隊列中。【D】調用condition.await()方法,此時【D】從AQS等待隊列中移除,並加入到condition對應的條件等待隊列中。【D】陷入等待之後,【T】啟動,由於AQS隊列中的【D】已經被移除,此時【T】也很快獲取到鎖,相應的,【T】也被加入到AQS等待隊列中。【T】接著調用condition.signal()方法,這時condition對應的條件隊列中只有一個節點【D】,於是【D】被取出,並被再次加入AQS的等待隊列中。此時【D】並沒有被喚醒,只是單純換了個位置。接著【T】執行lock.unlock(),釋放鎖鎖之後,會喚醒AQS隊列中的【D】,此時【D】真正被喚醒且執行。你看,【D】線程確實在兩個隊列中反覆橫跳吧,有關Condition的內容本文也只是拋磚引玉,之後會作詳細學習總結,如果你對這一系列感興趣,可以關注一下,後續會陸續對並發進行深入學習。

原文連結:https://www.cnblogs.com/summerday152/p/14238284.html

如果覺得本文對你有幫助,可以轉發關注支持一下

相關焦點

  • MySQL資料庫教程-數據表欄位約束
    MySQL資料庫教程-數據表欄位約束為保證資料庫中存儲數據的規範化,一般需要在定義欄位時進行欄位規範與約束的定義。在上圖中每一列稱為一個屬性或者欄位。其中第一行的每一列為欄位(屬性)名稱。第一行下面的每一行稱為記錄,即一條記錄。記錄是存儲結構化數據的基本單元。在實際進行欄位設置及值存儲過程中,需要結合實際情況確定每一個欄位存儲數據的格式,規範與要求。這些規範格式等即為欄位的約束。如學生的聯繫電話不允許出現重複,就需要定義與之對應的欄位約束。
  • ipv6頭部有多少欄位
    IPv6頭部有多少欄位?ipv6基本報頭包含40 bit,8個欄位。IPv6頭部結構遵循以下規則:1.基本頭部的固定長度:IPv6的基本報文頭長度固定為40位元組,選項和填充從基本報文頭中去除了。2.分段僅由流量的源節點處理:在發送IPv6流量前,源執行PMTU發現,將路由器從分段分組的任務中解脫出來。
  • 如何用SQL語句添加和修改欄位?
    用SQL語句添加欄位並不難,下面小編整理了SQL添加和修改欄位的基本命令,希望對各位小夥伴有所幫助。增加欄位:alter table 表名 add 欄位名 type not null default 0在指定位置插入新欄位:alter table 表名 add [column] 欄位名 欄位類型 是否可為空 comment '注釋' after
  • 你會用Oracle多欄位in嗎?
    in的用法也很普及,不過很少見到對多個欄位進行in操作本文就來分享一下如何在多個欄位中進行in操作先看看Oracle單個欄位使用in的場景SQL:select t.TABLE_NAME, t.TABLESPACE_NAME  from user_tables t
  • 基於CORS系統的高精度RTK定位技術設計詳解
    基於CORS系統的高精度RTK定位技術設計詳解 工程師青青 發表於 2018-07-25 15:51:50 0 引言 RTK定位技術是利用全球衛星導航系統
  • Excel數據透視表裡面容易忽視,計算項和計算欄位,很實用
    放在現有的工作表F1單元格然後將物品放在行標籤,將類型放在列標籤,將數量放至值中我們現在要求庫存,這個時候,就可以用到計算項這個功能,我們將單元格放在出庫的位置上,在上方的分析裡面,點擊計算,裡面有計算項,在彈出的窗口中,名稱為庫存,公式輸入=入庫-出庫,便可以得到所有的結果2、計算欄位
  • 解決thinkphp的表欄位是中文報錯的問題
    thinkphp提供了模型和資料庫操作資料庫表格數據,但是項目中遇到一個問題是一旦數據表欄位是中文時,就會報錯,"不支持的數據表達式"。數據表欄位是中文時,就會報錯這時怎麼辦呢?我通過查找資料基本上有修改thinkphp資料庫驅動的,但是沒成功。
  • CAD新手入門攻略:使用CAD軟體中欄位功能來預定義動態文字
    此時就需要使用浩辰CAD軟體中提供的欄位(field)功能了,接下來給大家介紹一下CAD新手入門攻略之使用欄位功能來預定義動態文字的操作技巧吧!欄位(field)功能預定義了一系列動態文字,設計人員只需在列表中選用即可,同時也支持Diesel表達式、LISP變量、系統變量來自定義動態文字。
  • 14 個實用的資料庫設計技巧,好用到爆
    主鍵與外鍵的設計,在全局資料庫的設計中,佔有重要地位。當全局資料庫的設計完成以後,有個美國資料庫設計專家說:「鍵,到處都是鍵,除了鍵之外,什麼也沒有」,這就是他的資料庫設計經驗之談,也反映了他對信息系統核心(數據模型)的高度抽象思想。因為:主鍵是實體的高度抽象,主鍵與外鍵的配對,表示實體之間的連接。 3.
  • 北京大學董蜀湘課題組利用「超材料」思想設計納米步進壓電馬達並取得重要進展
    北京大學工學院董蜀湘課題組受到「超材料」設計思想的啟發,設計了 [2×2] 多層陶瓷單元序構(OSPAU)結構的壓電驅動器,並通過多層壓電陶瓷共燒方法製備出壓電驅動器樣機。設計的壓電驅動器工作在人工準剪切d34模式,並表現出巨大的壓電係數(1403 pm/V),而在天然壓電陶瓷中該係數為零。
  • AEGx金瓦獎環球設計思想節 思想同行定見未來
    由金瓦獎組委會、旁觀者美學院主辦,重慶室內建築設計聯合會、九龍坡設計產業商會、木蘭會她設計聯盟、設計脊梁智庫聯合主辦的金瓦獎環球設計思想節將於8月13日在重慶大劇院閃耀啟幕。德國百年家電品牌AEG將以首席共建品牌的身份,出席這場主題名為「設計定見未來」的思想盛宴,與眾多設計大咖一起深度聚焦藝術設計與生活美學的豐富內涵。
  • 資料庫設計基礎:資料庫物理設計工作過程和設計步驟
    1、數據流物理設計的工作過程在資料庫的物理結構中,數據的基本單位是數據記錄,記錄以文件的形式進行存儲,一條存儲記錄對應關係模式中的一條邏輯記錄,並且文件當中還需要記錄存儲記錄的結構信息,比如欄位長度、數據類型、欄位描述等信息。
  • 中考重要考點二次函數的圖像與性質,分類詳解,歸納總結規律
    在初中的數學學習中,二次函數是非常重要的章節,而且裡面涉及的考點非常的多,不管是在對應學期的各種考試,還是在中考時,都是比較熱門的考點,而作為即將升入初三,面臨著新的知識,同學們更應該將這部分內容理解掌握,也有利於最後的複習,今天我和同學們一起學習中考比較重要的一個基礎考點,二次函數的圖像與性質
  • 濮良貴機械設計第10版複習筆記及詳解——才聰學習網
    [電子書]濮良貴《機械設計》(第10版)筆記和課後習題(含考研真題)詳解本章是濮良貴主編的《機械設計》(第10版)教材的學習輔導書,主要包括以下內容:1.整理名校筆記,濃縮內容精華。本書參考了該教材的國內外配套資料和其他教材的相關知識對該教材的課(章)後習題進行了詳細的分析和解答,並對相關重要知識點進行了延伸和歸納。3.挑選考研真題,總結出題思路。本書挑選了部分名校的相關考研真題,總結出題思路,有利於強化對重要知識點的理解。
  • Excel數據透視表高級篩選用欄位和公式組合多個條件並篩選到新表
    像在 Excel 普通表格一樣,高級篩選條件區域既可以用單個條件也可以用多個條件;多個條件的組合方式分為用欄位組合與用公式組合;如果條件比較複雜,用欄位不好組合,可以用公式組合。在數據透視表中,除可篩選普通的欄位外,還可以把分類匯總結果一起篩選。以下就是Excel數據透視表高級篩選用欄位和公式組合多個條件並且把篩選結果複製到新工作表的具體操作方法,實例中操作所用版本均為 Excel 2016。
  • 汙水處理知識篇:曝氣生物濾池設計計算詳解
    北極星水處理網訊:汙水處理,作為環境保護的重要組成部分,目前眾多汙水處理工藝相結合而統一進行處理汙水,本文將為詳解曝氣生物濾池設計計算,以便大家進行詳細了解。一、設計條件1、進水水質情況Q=12000m³/dCOD≤60mg/LBOD5≤30mg/L總氮TN≤50mg/L(氨氮+亞硝酸鹽氮+硝酸鹽氮+有機氮)總凱式氮KN≤40mg/L(氨氮+有機氮)亞硝酸鹽氮、硝酸鹽氮:10 mg/L氨氮25 mg
  • 解讀科學發展觀重要思想的「四大精髓」
    黨的十八大順應全黨的呼聲意願,站在歷史和時代的高度,把科學發展觀確立為黨必須長期堅持的指導思想,並鄭重寫入黨章,這無疑是我黨政治生活的一件大事。科學發展觀作為中國特色社會主義理論體系最新成果,是與鄧小平理論和「三個代表」重要思想既一脈相承又與時俱進的科學體系,更是指導黨和國家全部工作的強大思想武器,對新形勢下實現什麼樣的發展、怎樣發展等重大問題作出了回答。
  • 概念設計和細節設計為什麼在產品設計中都很重要?
    在產品設計開發過程中,工業設計公司的設計團隊可以進行概念設計和詳細設計。了解兩者之間的差異及其在產品設計中的作用對於成功實現任何產品概念至關重要。為什麼兩者在產品設計中都很重要呢?我們一起來看看。產品設計1.什麼是概念設計?從根本上說,概念設計是產品設計過程的第一階段,其中使用了圖紙和其他插圖或模型。
  • 高考物理最重要的十種解題思想方法(含例題詳解),列印收藏!
    有人定義,物理思想方法就是人們對自然物質及其運動規律的認識方法,是源於物理世界又指導人們對物理世界進行再認識、再改造和實踐應用的思維體系,是辯證唯物主義的方法論和認識論在物理學中的具體體現。物理學中的思想方法,是求解物理問題的根本所在。認真研究總結物理學中的思想方法、策略技巧,並能在實際解題過程中靈活應用,可收到事半功倍的效果。
  • 恩格斯重要思想以前可能忽略了
    中央編譯局原常務副局長顧錦屏指出,今天我們對恩格斯最好的紀念,就是全面、系統地闡述恩格斯的生平事業和理論創新,要闡明恩格斯同馬克思一起創立馬克思主義的科學理論體系,要凸顯恩格斯晚年對馬克思主義作出的理論拓新,有力回應馬克思恩格斯思想「對立論」等論調,這對於我們全面理解21世紀馬克思主義理論具有重要價值。