多線程編程(3)——synchronized原理以及使用

一、對象頭

  通常在java中一個對象主要包含三部分:

  • 對象頭 主要包含GC的狀態、、類型、類的模板信息(地址)、synchronization狀態等,在後面介紹。

  • 實例數據:程序代碼中定義的各種類型的字段內容。

  • 對齊數據:對象的大小必須是 8 字節的整數倍,此項根據情況而定,若對象頭和實例數據大小正好是8的倍數,則不需要對齊數據,否則大小就是8的差數。

先看下面的實例、程序的輸出以及解釋。

/*需提前引入jar包
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core 解析java對象布局 -->
        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.9</version>
        </dependency>
​
*/
//Java對象以8個字節對其,不夠則使用對其數據
public class Student {
    private int id;       // 4字節
    private boolean sex;  // 1字節
    public Student(int id, boolean sex){
        this.id = id;
        this.sex = sex;
    }
}
public class Test01 {
    public static void main(String[] args) {
        Student stu = new Student(6, true);
        //計算對象hash,底層是C++實現,不需要java去獲取,如果此處不調用,則後面的hash值不會去計算
        System.out.println("hashcode: " + stu.hashCode());  
        System.out.println(ClassLayout.parseInstance(stu).toPrintable());
    }
}
/* output
hashcode: 523429237
com.thread.synchronizeDemo.Student object internals:
OFFSET SIZE TYPE DESCRIPTION        VALUE
 0     4    (object header)    01 75 e5 32 (00000001 01110101 11100101 00110010) (853898497)
4      4     (object header)    1f 00 00 00 (00011111 00000000 00000000 00000000) (31)
8      4     (object header)    43 c1 00 20 (01000011 11000001 00000000 00100000) (536920387)
12     4       int Student.id                                6
16     1   boolean Student.sex                               true
17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total
​
備註:上述代碼在64位的機器上運行,此時
對象頭占  (4+4+4)*8 = 96 位(bit)
實例數據  (4+1)*8 = 40 位(bit)
對齊數據  7*8 = 56 位(bit) 因為Java對象以8個字節對其的方式,需補7byte去對齊
*/

   下面主要陳述對對象頭的解釋,內容從hotspot官網摘抄下來的信息:

object header

  Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object’s layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format.

mark word

  The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.

klass pointer

  The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original objec

  由此可知,對象頭主要包含GC的狀態(用4位表示——表示範圍0-15,用來記錄GC年齡,這也就是為什麼對象在survivor中從from區到to區來迴轉換15次後轉入到老年代tenured區)、類型、類的模板信息(地址)、synchronization 狀態等,由兩個字組成mark word和klass pointer(類元素據信息地址,具體數據通常在堆的方法區中,即8字節,但有時候會有一些優化設置,會開啟指針壓縮,將代表klass pointer的8字節變成4字節大小,這也是為什麼在上述代碼中對象頭大小是(8+4)byte,而不是16byte。)。本節最主要介紹對象頭的mark word這部分。關於對象頭中每部分bit所代表的意義可以查看hotspot源碼中代碼的注,這段註釋是從openjdk中拷貝的。

JVM和hotspot、openjdk的區別

JVM是一種產品的規範定義,hotspot(Oracle公司)是對該規範實現的產品,還有遵循這些規範的其他產品,比如J9(IBM開發的一個高度模塊化的JVM)、Zing VM等。

openjdk是一個hotspot項目的大部分源代碼(可以通過編譯后變成.exe文件),hotspot小部分代碼Oracle並未公布

// openjdk-8-src-b132-03_mar_2014\openjdk\hotspot\src\share\vm\oops\markOop.hpp
/*
Bit-format of an object header (most significant first, big endian layout below):
​
32 bits:
--------
        hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
        JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
        size:32 ------------------------------------------>| (CMS free block)
        PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
​
64 bits:
--------
unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
size:64 ----------------------------------------------------->| (CMS free block)
​
*/

可以看到在32位機器和64位機器中,對象的布局的差異還是很大的,本文主要 敘述64位機器下的布局,其實兩者無非是位數不同而已,大同小異。在64位機器用64位(8byte)表示Mark Word,首先前25位(0-25)是未被使用,接下來31位表示hash值,然後是對象分代年齡大小,最後Synchronized的鎖信息,分為兩部分,共3bit,如下錶,鎖的嚴格性依次是鎖、偏向鎖、輕量級鎖、重量級鎖。

 

關於鎖的一些解釋

無鎖

  無鎖沒有對資源進行鎖定,所有的線程都能訪問並修改同一個資源,但同時只有一個線程能修改成功。

偏向鎖

  引入偏向鎖是為了在無多線程競爭的情況下,一段同步代碼一直被一個線程所訪問因為輕量級鎖的獲取及釋放依賴多次CAS原子指令,而偏向鎖只需要在置換ThreadID的時候依賴一次CAS原子指令,當由另外的線程所訪問,偏向鎖就會升級為輕量級鎖。

輕量級鎖 

  當鎖是偏向鎖的時候,被另外的線程所訪問,偏向鎖就會升級為輕量級鎖,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞,從而提高性能。輕量級鎖所適應的場景是線程交替執行同步塊的情況,如果存在同一時間訪問同一鎖的情況,就會導致輕量級鎖膨脹為重量級鎖。

重量級鎖

  依賴於操作系統Mutex Lock所實現的鎖,JDK中對Synchronized做的種種優化,其核心都是為了減少這種重量級鎖的使用。JDK1.6以後,為了減少獲得鎖和釋放鎖所帶來的性能消耗,提高性能,引入了“輕量級鎖”和“偏向鎖”。  

GC

  這並不是鎖的狀態,而是GC標誌,等待GC回收。

現在開始從程序層面分析前面程序的對象頭的布局信息,在此之前需要知道的是,在windows中對於數據的存儲採用的是小端存儲,所以要反過來讀

大端模式——是指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的高地址中,這樣的存儲模式有點兒類似於把數據當作字符串順序處理:地址由小向大增加,而數據從高位往低位放;這和我們的閱讀習慣一致。

小端模式——是指數據的高字節保存在內存的高地址中,而數據的低字節保存在內存的低地址中,這種存儲模式將地址的高低和數據位權有效地結合起來,高地址部分權值高,低地址部分權值低。 一般在網絡中用的大端;本地用的小端;

運行程序如下,可以看到對應的hashcode值被打印出來:

public static void main(String[] args) {
     Student stu = new Student(6, true);
    //Integer.toHexString()此方法返回的字符串表示的無符號整數參數所表示的值以十六進制
     System.out.println("hashcode: " + Integer.toHexString(stu.hashCode()));
     System.out.println(ClassLayout.parseInstance(stu).toPrintable());
}
/*
hashcode: 1f32e575
com.thread.synchronizeDemo.Student object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 75 e5 32 (00000001 01110101 11100101 00110010) (853898497)
      4     4           (object header)                           1f 00 00 00 (00011111 00000000 00000000 00000000) (31)
      8     4           (object header)                           43 c1 00 20 (01000011 11000001 00000000 00100000) (536920387)
     12     4       int Student.id                                6
     16     1   boolean Student.sex                               true
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

//前8個字節反過來看,可以看出對象頭的hash是1f32e575,同時是無鎖的狀態00000001
*/

 二、Monitor

       可以把它理解為一個同步工具(數據結構),也可以描述為一種同步機制,通常被描述為一個對象。每個對象都存在着一個 monitor 與之關聯,對象與其 monitor 之間的關係有存在多種實現方式,如monitor可以與對象一起創建銷毀或當線程試圖獲取對象鎖時自動生成,但當一個 monitor 被某個線程持有后,它便處於鎖定狀態(每一個線程都有一個可用 monitor record 列表)[具體可以看參考資料5]。需要注意的是這種監視器鎖是發生在對象的內部鎖已經變成重量級鎖的時候。

/*  openjdk-8-src-b132-03_mar_2014\openjdk\hotspot\src\share\vm\runtime\ObjectMonitor.hpp
// initialize the monitor, exception the semaphore, all other fields // are simple integers or pointers ObjectMonitor() { _header = NULL; _count = 0; //記錄個數 _waiters = 0, _recursions = 0; _object = NULL; _owner = NULL; _WaitSet = NULL; //處於wait狀態的線程,會被加入到_WaitSet _WaitSetLock = 0 ; _Responsible = NULL ; _succ = NULL ; _cxq = NULL ; FreeNext = NULL ; _EntryList = NULL ; //處於等待鎖block狀態的線程,會被加入到該列表 _SpinFreq = 0 ; _SpinClock = 0 ; OwnerIsThread = 0 ; _previous_owner_tid = 0; } */

  Monitor的實現主要藉助三個結構去完成多線程的併發操作——_owner、_WaitSet 、_EntryList。當多個線程同時訪問由synchronized修飾的對象、類或一段同步代碼時,首先會進入_EntryList 集合,如果某個線程取得了_owner的所有權,該線程就可以去執行,如果該線程調用了wait()方法,就會放棄_owner的所有權,進入等待狀態,等下一次喚醒。如下圖(圖片摘自參考資料5)。

 三、synchronized的用法

     synchronized修飾方法和修飾一個代碼塊類似,只是作用範圍不一樣,修飾代碼塊是大括號括起來的範圍,而修飾方法範圍是整個函數。其中synchronized(this) 與synchronized(class) 之間的區別有以下五點要注意:

    1、對於靜態方法,由於此時對象還未生成,所以只能採用類鎖;

    2、只要採用類鎖,就會攔截所有線程,只能讓一個線程訪問。

    3、對於對象鎖(this),如果是同一個實例,就會按順序訪問,但是如果是不同實例,就可以同時訪問。

   4、如果對象鎖跟訪問的對象沒有關係,那麼就會都同時訪問。

   5、當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。

當然,Synchronized也可修飾一個靜態方法,而靜態方法是屬於類的而不屬於對象的,所以synchronized修飾的靜態方法鎖定的是這個類的所有對象。關於如下synchronized的用法,我們經常會碰到的案例:

public class Thread5 implements Runnable {
    private static int count = 0;
    public synchronized static void add() {
        count++;
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000000; i++) {
            synchronized (Thread5.class){
                count++;
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ExecutorService es = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 20; i++) {
            es.execute(new Thread5());
        }
        es.shutdown();
        es.awaitTermination(6, TimeUnit.SECONDS);
        System.out.println(count);
    }
}
/* 類鎖
  20000000
  */

而一旦換成對象鎖,不同實例,就可以同時訪問。則會出錯:

public void run() {
        for (int i = 0; i < 1000000; i++) {
            synchronized (this){
                count++;
            }
        }
}
/* 對象鎖
 10746948
*/

這是因為靜態變量並不屬於某個實例對象,而是屬於類所有,所以對某個實例加鎖,並不會改變count變量臟讀和臟寫的情況,還是造成結果不正確。

 

參考資料

  1. 對象布局的各部分介紹——

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

[學習筆記] 在Eclipse中使用Hibernate,並創建第一個Demo工程,數據庫為Oracle XE

前文參考:

在Eclipse中使用Hibernate

安裝 Hibernate Tools 插件

https://tools.jboss.org/downloads/

Add the following URL to your Eclipse 4.13 (2019-09) installation, via:

Help > Install New Software… > Work with:

http://download.jboss.org/jbosstools/photon/stable/updates/

Then select the individual features that you want to install:

點擊Next

點擊Next
同意相關協議,點擊Finish .

則會開始下載安裝。

視網絡速度,可能需要幾分鐘到十幾分鐘的時間才能完成安裝。

最後會提示重啟Eclipse才能生效。

在Eclipse中新建Hibernate應用

File->New -> Java Project

點擊Finish

項目結構圖

在Eclipse中新建用戶庫

此時下面显示了已經建立的用戶庫列表

我們要添加Hibernate的依賴庫,因此點擊用戶庫

Hibernate_4.3.5_final

選擇jar文件

項目結構圖

繼續配置Hibernate

最後自動形成 如下的文件內容:[本例使用oracle數據庫]

Oracle 11g xe 在windows安裝請看如下鏈接:

hibernate.cfg.xml

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
        <property name="hibernate.connection.password">123456</property>
        <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:xe</property>
        <property name="hibernate.connection.username">test</property>
        <property name="hibernate.default_schema">test</property>
        <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
    </session-factory>
</hibernate-configuration>

再增加幾個屬性

配置文件更新后的內容如下: 注意要去掉name屬性 更改 為

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                                         "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
 <session-factory >
  <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
  <property name="hibernate.connection.password">123456</property>
  <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:xe:orcl</property>
  <property name="hibernate.connection.username">test</property>
  <property name="hibernate.default_schema">test</property>
  <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
  <property name="hibernate.show_sql">true</property>
  <!-- 第一次加載hibernate時根據model類會自動建立起表的結構(前提是先建立好數據庫),以後加載hibernate時根據 model類自動更新表結構,即使表結構改變了但表中的行仍然存在不會刪除以前的行。要注意的是當部署到服務器后,表結構是不會被馬上建立起來的,是要等 應用第一次運行起來后才會。 -->
  <property name="hibernate.hbm2ddl.auto">update</property> 
  <property name="hibernate.format_sql">true</property>
  <property name="hibernate.default_entity_mode">pojo</property>
 </session-factory>
</hibernate-configuration>

繼續完善工程Hibernate_demo_001

新建一個包:mytest001.demo

在包mytest001.demo之下新建一個PO類: Emp

package mytest001.demo;

public class Emp {
    // 員工的標識屬性
    private Integer id;
    // 姓名
    private String name;
    // 年齡
    private Integer age;
    // 工資 (分)
    private Integer salary;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSalary() {
        return salary;
    }

    public void setSalary(Integer salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Emp [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
    }
}

此刻此PO Emp.java 尚不具備持久化能力。下面為其添加註解。

@Entity 註解聲明該類是一個Hibernate持久化類
@Table 指定該類映射的表,對應的數據庫表名是T_EMP
@Id 指定該類的標識屬性,映射到數據庫的主鍵列
@GeneratedValue(strategy=GenerationType.SEQUENCE) 指定了主鍵生成策略,由於本文使用Oracle Database, 因此指定了使用 SEQUENCE

在hibernate.cfg.xml中增加持久化映射類名

增加一個新類:EmpManager,用於管理員工。

package mytest001.demo;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.Service;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;


public class EmpManager {

    public static void main(String[] args)  throws Exception  {
         
    //實例化配置
    Configuration configuration  = new Configuration()
            //不帶參數則默認加載hibernate.cfg.xml
            .configure();
    
    ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
            .applySettings(configuration.getProperties()).build();
    SessionFactory sFactory = configuration.buildSessionFactory(serviceRegistry);
    
    //創建session 
    Session session = sFactory.openSession();
    
    //開始事務
    Transaction tx = session.beginTransaction();
    //創建員工對象
    Emp emp = new Emp();
    // 設置員工信息
    emp.setAge(28);
    emp.setName("scott");
    emp.setSalary(10000);
    session.save(emp);
    // 提交事務
    tx.commit();
    session.close();
    sFactory.close();
            
    
    }

}

最後配置文件的內容:hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                                         "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
 <session-factory >
  <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
  <property name="hibernate.connection.password">123456</property>
  <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:xe</property>
  <property name="hibernate.connection.username">test</property>
  <property name="hibernate.default_schema">test</property>
  <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
  <property name="hibernate.show_sql">true</property>
  <!-- 第一次加載hibernate時根據model類會自動建立起表的結構(前提是先建立好數據庫),以後加載hibernate時根據 model類自動更新表結構,即使表結構改變了但表中的行仍然存在不會刪除以前的行。要注意的是當部署到服務器后,表結構是不會被馬上建立起來的,是要等 應用第一次運行起來后才會。 -->
  <property name="hibernate.hbm2ddl.auto">update</property> 
  <property name="hibernate.format_sql">true</property>
  <property name="hibernate.default_entity_mode">pojo</property>
  <mapping class="mytest001.demo.Emp"/>
 </session-factory>
</hibernate-configuration>

Emp.java

package mytest001.demo;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="T_EMP")
public class Emp {
    // 員工的標識屬性
    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE)
    private Integer id;
    // 姓名
    private String name;
    // 年齡
    private Integer age;
    // 工資 (分)
    private Integer salary;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSalary() {
        return salary;
    }

    public void setSalary(Integer salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Emp [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
    }
}

最後的工程結構如下:

運行EmpManger

十一月 23, 2019 8:50:47 上午 org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.4.Final}
十一月 23, 2019 8:50:47 上午 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.3.5.Final}
十一月 23, 2019 8:50:47 上午 org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
十一月 23, 2019 8:50:47 上午 org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
十一月 23, 2019 8:50:47 上午 org.hibernate.cfg.Configuration configure
INFO: HHH000043: Configuring from resource: /hibernate.cfg.xml
十一月 23, 2019 8:50:47 上午 org.hibernate.cfg.Configuration getConfigurationInputStream
INFO: HHH000040: Configuration resource: /hibernate.cfg.xml
十一月 23, 2019 8:50:47 上午 org.hibernate.cfg.Configuration doConfigure
INFO: HHH000041: Configured SessionFactory: null
十一月 23, 2019 8:50:47 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH000402: Using Hibernate built-in connection pool (not for production use!)
十一月 23, 2019 8:50:47 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000401: using driver [oracle.jdbc.driver.OracleDriver] at URL [jdbc:oracle:thin:@localhost:1521:xe]
十一月 23, 2019 8:50:47 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000046: Connection properties: {user=test, password=****}
十一月 23, 2019 8:50:47 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000006: Autocommit mode: false
十一月 23, 2019 8:50:47 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
十一月 23, 2019 8:50:48 上午 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.Oracle10gDialect
十一月 23, 2019 8:50:48 上午 org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000399: Using default transaction strategy (direct JDBC transactions)
十一月 23, 2019 8:50:48 上午 org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000228: Running hbm2ddl schema update
十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000102: Fetching database metadata
十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000396: Updating schema
十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
INFO: HHH000262: Table not found: T_EMP
十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
INFO: HHH000262: Table not found: T_EMP
十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
INFO: HHH000262: Table not found: T_EMP
十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000232: Schema update complete
Hibernate: 
    select
        test.hibernate_sequence.nextval 
    from
        dual
Hibernate: 
    insert 
    into
        test.T_EMP
        (age, name, salary, id) 
    values
        (?, ?, ?, ?)
十一月 23, 2019 8:50:48 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:oracle:thin:@localhost:1521:xe]

到數據庫中查詢表:(這個表會被自動創建)
select * from t_emp;

如果再次運行會增加新的記錄。

至此,本文完成。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

javascript閉包詳解

閉包(closure)是Javascript語言的一個難點,也是它的特色,很多高級應用都要依靠閉包實現。

下面就是我的學習筆記,對於Javascript初學者應該是很有用的。

一、變量的作用域

要理解閉包,首先必須理解Javascript特殊的變量作用域。

變量的作用域無非就是兩種:全局變量和局部變量。

Javascript語言的特殊之處,就在於函數內部可以直接讀取全局變量。

  

var n=999;

  function f1(){
    alert(n);
  }

  f1(); // 999

  

另一方面,在函數外部自然無法讀取函數內的局部變量。

 

 function f1(){
    var n=999;
  }

  alert(n); // error

  

這裡有一個地方需要注意,函數內部聲明變量的時候,一定要使用var命令。如果不用的話,你實際上聲明了一個全局變量!

 

 function f1(){
    n=999;
  }

  f1();

  alert(n); // 999

  

二、如何從外部讀取局部變量?

出於種種原因,我們有時候需要得到函數內的局部變量。但是,前面已經說過了,正常情況下,這是辦不到的,只有通過變通方法才能實現。

那就是在函數的內部,再定義一個函數。

  

function f1(){

    var n=999;

    function f2(){
      alert(n); // 999
    }

  }

  

在上面的代碼中,函數f2就被包括在函數f1內部,這時f1內部的所有局部變量,對f2都是可見的。但是反過來就不行,f2內部的局部變量,對f1就是不可見的。這就是Javascript語言特有的”鏈式作用域”結構(chain scope),子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立。

既然f2可以讀取f1中的局部變量,那麼只要把f2作為返回值,我們不就可以在f1外部讀取它的內部變量了嗎!

 

 function f1(){

    var n=999;

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

  

三、閉包的概念

上一節代碼中的f2函數,就是閉包。

各種專業文獻上的”閉包”(closure)定義非常抽象,很難看懂。我的理解是,閉包就是能夠讀取其他函數內部變量的函數。

由於在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成”定義在一個函數內部的函數”。

所以,在本質上,閉包就是將函數內部和函數外部連接起來的一座橋樑。

四、閉包的用途

閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中。

怎麼來理解這句話呢?請看下面的代碼。

  function f1(){

    var n=999;

    nAdd=function(){n+=1}

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

  nAdd();

  result(); // 1000

在這段代碼中,result實際上就是閉包f2函數。它一共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數f1中的局部變量n一直保存在內存中,並沒有在f1調用后被自動清除。

為什麼會這樣呢?原因就在於f1是f2的父函數,而f2被賦給了一個全局變量,這導致f2始終在內存中,而f2的存在依賴於f1,因此f1也始終在內存中,不會在調用結束后,被垃圾回收機制(garbage collection)回收。

這段代碼中另一個值得注意的地方,就是”nAdd=function(){n+=1}”這一行,首先在nAdd前面沒有使用var關鍵字,因此nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(anonymous function),而這個匿名函數本身也是一個閉包,所以nAdd相當於是一個setter,可以在函數外部對函數內部的局部變量進行操作。

五、使用閉包的注意點

1)由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。

2)閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。

六、思考題

如果你能理解下面兩段代碼的運行結果,應該就算理解閉包的運行機制了。

代碼片段一。

  var name = “The Window”;

  var object = {
    name : “My Object”,

    getNameFunc : function(){
      return function(){
        return this.name;
      };

    }

  };

  alert(object.getNameFunc()());

代碼片段二。

  var name = “The Window”;

  var object = {
    name : “My Object”,

    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };

    }

  };

  alert(object.getNameFunc()());

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

Vue項目使用CSS變量實現主題化

主題化管理經常能在網站上看到,一般的思路都是將主題相關的CSS樣式獨立出來,在用戶選擇主題的時候加載相應的CSS樣式文件。現在大部分瀏覽器都能很好的兼容,主題化樣式更容易管理了。最近,使用CSS變量在Vue項目中做了一個主題化實踐,下面來看看整個過程。

可行性測試

為了檢驗方法的可行性,在public文件夾下新建一個themes文件夾,並在themes文件夾新建一個default.css文件:

:root {
  --color: red;
}

在public文件夾的index.html文件中引入外部樣式theme.css,如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>vue-skin-peeler-demo</title>
    <!-- 引入themes文件夾下的default.css -->
    <link rel="stylesheet" type="text/css" href="src/themes/default.css" rel="external nofollow">
  </head>
  <body>
    <noscript>
      <strong>We're sorry but vue-skin-peeler-demo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

然後,在Home.vue中使用CSS變量:

<template>
  <div class="home">
    <div :class="$style.demo">變紅色</div>
  </div>
</template>

<script>
export default {
  name: 'home'
}
</script>

<style module lang="scss">
  .demo {
    color: var(--color);
  }
</style>

然後,運行項目並在瀏覽器中打開頁面,頁面显示效果正常。

注意:@vue/cli使用link標籤引入css樣式可能報錯“We’re sorry but vue-skin-peeler-demo doesn’t work properly without JavaScript enabled. Please enable it to continue.”。這是因為@vue/cli將src目錄下的文件都通過webpack打包所引起,所以,靜態文件資源要放在public(如果是@vue/cli 2.x版本放在static)文件夾下。

實現主題切換

這裏主題切換的思路是替換link標籤的href屬性,因此,需要寫一個替換函數,在src目錄下新建themes.js文件,代碼如下:

// themes.js
const createLink = (() => {
  let $link = null
  return () => {
    if ($link) {
      return $link
    }
    $link = document.createElement('link')
    $link.rel = 'stylesheet'
    $link.type = 'text/css'
    document.querySelector('head').appendChild($link)
    return $link
  }
})()

/**
 * 主題切換函數
 * @param {string} theme - 主題名稱, 默認default
 * @return {string} 主題名稱
 */
const toggleTheme = (theme = 'default') => {
  const $link = createLink()
  $link.href = `./themes/${theme}.css`
  return theme
}

export default toggleTheme

然後,在themes文件下創建default.css和dark.css兩個主題文件。創建CSS變量,實現主題化。CSS變量實現主題切換請參考另一篇文章

兼容性

IE瀏覽器以及一些舊版瀏覽器不支持CSS變量,因此,需要使用,是一個,可在舊版和現代瀏覽器中為CSS自定義屬性(也稱為“ CSS變量”)提供客戶端支持。由於要開啟watch監聽,所以還有安裝。

安裝:

npm install css-vars-ponyfill mutationobserver-shim --save

然後,在themes.js文件中引入並使用:

// themes.js
import 'mutationobserver-shim'
import cssVars from 'css-vars-ponyfill'

cssVars({
  watch: true
})

const createLink = (() => {
  let $link = null
  return () => {
    if ($link) {
      return $link
    }
    $link = document.createElement('link')
    $link.rel = 'stylesheet'
    $link.type = 'text/css'
    document.querySelector('head').appendChild($link)
    return $link
  }
})()

/**
 * 主題切換函數
 * @param {string} theme - 主題名稱, 默認default
 * @return {string} 主題名稱
 */
const toggleTheme = (theme = 'default') => {
  const $link = createLink()
  $link.href = `./themes/${theme}.css`
  return theme
}

export default toggleTheme

開啟watch后,在IE 11瀏覽器點擊切換主題開關不起作用。因此,每次切換主題時都重新執行cssVars(),還是無法切換主題,原因是開啟watch后重新執行cssVars()是無效的。最後,只能先關閉watch再重新開啟。成功切換主題的themes.js代碼如下:

// themes.js
import 'mutationobserver-shim'
import cssVars from 'css-vars-ponyfill'

const createLink = (() => {
  let $link = null
  return () => {
    if ($link) {
      return $link
    }
    $link = document.createElement('link')
    $link.rel = 'stylesheet'
    $link.type = 'text/css'
    document.querySelector('head').appendChild($link)
    return $link
  }
})()

/**
 * 主題切換函數
 * @param {string} theme - 主題名稱, 默認default
 * @return {string} 主題名稱
 */
const toggleTheme = (theme = 'default') => {
  const $link = createLink()
  $link.href = `./themes/${theme}.css`
  cssVars({
    watch: false
  })
  setTimeout(function () {
    cssVars({
      watch: true
    })
  }, 0)
  return theme
}

export default toggleTheme

查看所有代碼,請移步。

記住主題

實現記住主題這個功能,一是可以向服務器保存主題,一是使用本地存儲主題。為了方便,這裏主要使用本地存儲主題的方式,即使用localStorage存儲主題。具體實現請移步。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

Tesla超級充電站計畫開放給他牌電動車充電

 

由於各大廠牌的電動車充電標準不同,加上充電站設立不足,續航力一直是電動車普及化的最大障礙之一,但這個情況隨著特斯拉的投入,或許未來即將有望改變。

Electrek 報導,特斯拉過去經常談到將超級充電站網路(Supercharger network)開放給其他車商的可能,但一直都沒有後續消息傳出。就在16 日能源博覽會中,技術長JB Straubel 再度提及這個想法,他表示,目前正與其他車商針對充電站的設置「積極交流」中。

特斯拉最早提到這個概念是在2015 年9 月,當時執行長馬斯克(Elon Musk)表示,特斯拉會持續擁有並經營所有超級充電站,其他車商只需要為該廠牌電動車的充電費用付費即可。

身為世界領先的電動車大廠,特斯拉的超級充電站無論充電率、覆蓋率都領先其他車廠許多,如果這樣的合作方式成真,相信對電動車普及會很有幫助,只怕充電站的塞車情況會更嚴重。

特斯拉一直有在擴大超級充電站網路,根據官網介紹,目前全球已有超過861 個特斯拉超級充電站,但充電站塞車的情況仍舊持續上演,更別提目前多數電動車充電率都沒辦法達到特斯拉的一半;一旦開放其他廠牌車輛使用,車主恐怕得在充電站等上更久時間。

為了改善這種情況,除了宣布進一步擴展網路以外,特斯拉推出一種新型用戶付費系統,可以提供其他車商的用戶使用。值得一提的是,特斯拉也加入了CCS 標準協會,這意味著未來超級充電站的充電接口兼容性或許會更廣泛。

除此之外,一些車商正在打造更高充電率的電動車,很快特斯拉就不會是唯一能以100kW+ 充電率充電的車輛,包括保時捷、奧迪、賓士都宣布,未來2 年會推出新款電動車,估計這些車輛能接受直流快速充電。

(合作媒體:。圖片出處:wikipedia)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

市場電動車需求上升,Nissan將對現有車款EV化

日經新聞報導,日產汽車(Nissan)將大舉擴充電動車(EV)產品陣容,日產社長兼CEO西川廣人27日於橫濱市舉行的定期股東會上表示,「今年度將推出新型『Leaf』,且中期來看,將推動現行已進行量產販售的車款EV化」。

因北美、中國加強環保規範,帶動EV有望進一步普及。日產目前的EV車款僅有「Leaf」等少數幾款,而之後計畫將SUV、輕型汽車以及商用車進行EV化。

另外,日產會長Carlos Ghosn也在股東會上表示,「日產在EV界居領導位置。日產EV累計銷售量超過60萬台、為美國特斯拉(Tesla)的2倍」。

日本市調機構富士經濟(Fuji Keizai)6月22日公布銷售動向報告指出,EV在2025年以後需求將急速增加,預估2030年時EV年銷售量將增至407萬台、超越油電混合車(HV、2030年銷售量預估為391萬台),且之後雙方的差距將持續擴大。在中國需求增加加持下,2035年EV全球銷售量將擴大至630萬台、將達2016年的13.4倍(較2016年增加12.4倍)。

(本文內容由授權使用。圖片出處:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

樹莓派3B/3B+和4B安裝OpenCV教程 (屢試不爽)

    

安裝前準備

  1. 在樹莓派上拓展文件系統

    如果你使用的樹莓派為新裝的系統,那麼第一件事情就是擴展文件系統,以包括microSD卡上的所有空間。

    具體步驟如下:

      1.在樹莓派終端(或者SSH)上輸入:

1 $ sudo raspi-config

 

      2.然後選擇“高級選項”菜單項

   

       3.然後選擇“擴展文件系統”:

  

       4. 選擇第一個選項“A1.Expand Filesystem”,按鍵盤上的Enter鍵,完成後點擊“Finish”按鈕,重新啟動樹莓派。

    如果不能重啟,則可以執行以下操作:

1 $ sudo reboot

 

      重新啟動后,文件系統已經擴展為包括micro-SD卡上的所有空間。可以通過執行 df -h 檢查輸出來驗證磁盤是否已擴展。

1 $ df -h

 

 

  

 

      5. 此時我的樹莓派文件系統已擴展為包含16GB的micor-SD卡。如果您使用的是8GB卡,則可能使用了將近50%的可用空間,

    因此,一件簡單的事情就是刪除LibreOffice和Wolfram引擎以釋放Pi上的一些空間:

1 $ sudo apt-get purge wolfram-engine
2 $ sudo apt-get purge libreoffice*
3 $ sudo apt-get clean
4 $ sudo apt-get autoremove

 

 

  2.更換樹莓派源為清華鏡像源,防止後面下載GTK2.0失敗。

    換源方法參考:

安裝步驟

   1.更新系統

1 $ sudo apt-get update && sudo apt-get upgrade

   

   2.在樹莓派上安裝OpenCV所需要依賴的工具和一些圖像視頻庫

  • 安裝包括CMake的開發人員工具

    1 // 安裝build-essential、cmake、git和pkg-config
    2 sudo apt-get install build-essential cmake git pkg-config 

     

  • 安裝常用圖像工具包

    1 // 安裝jpeg格式圖像工具包
    2 sudo apt-get install libjpeg8-dev 

     

    1 // 安裝tif格式圖像工具包
    2 sudo apt-get install libtiff5-dev 

     

    1 // 安裝JPEG-2000圖像工具包
    2 sudo apt-get install libjasper-dev 

     

    1 // 安裝png圖像工具包
    2 sudo apt-get install libpng12-dev 

     

  • 安裝常用的視頻庫

1 //v4l中4後面的是 英文字母“l”
2 sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev

 

  

  • 安裝GTK2.0

    1 sudo apt-get install libgtk2.0-dev

     

  • 安裝OpenCV數值優化函數包

1 sudo apt-get install libatlas-base-dev gfortran

 

    

 

    3.下載編譯OpenCV源碼

  • 下載opencv3.4.3和opencv_contrib3.4.3

1 // 下載OpenCV
2 wget -O opencv-3.4.3.zip https://github.com/Itseez/opencv/archive/3.4.3.zip

 

   

1 // 解壓OpenCV
2 unzip opencv-3.4.3.zip

 

   

1 // 下載OpenCV_contrib庫:
2 wget -O opencv_contrib-3.4.3.zip https://github.com/Itseez/opencv_contrib/archive/3.4.3.zip

 

   

 

1 // 解壓OpenCV_contrib庫:
2 unzip opencv_contrib-3.4.3.zip

   

  • 配置CMake編譯OpenCV 3環境

    使用CMake設置編譯,然後運行 make 來編譯OpenCV。這是整個過程中耗時最長的步驟,大約4個小時。

    回到OpenCV存儲庫並創建 build 文件夾,用來存放 CMake 編譯時產生的臨時文件。

1 //具體路徑請以實際為準
2 cd ~/opencv-3.4.3
3 
4 // 新建build文件夾
5 mkdir build
6      
7 // 進入build文件夾
8 cd build
9   

   

 

  •  設置CMake編譯參數,安裝目錄默認為/usr/local

    注意參數名、等號和參數值之間不能有空格,每行末尾“\”之前有空格,這裏使用換行符“\”是為了看起來工整,參數值最後是兩個英文的點,意思是上級

  目錄(【注意】如果在root用戶下執行cmake命令,請將OPENCV_EXTRA_MODULES_PATH的值改為絕對路徑,如:/home/pi/opencv_contrib-3.4.3/modules):

/** CMAKE_BUILD_TYPE是編譯方式
* CMAKE_INSTALL_PREFIX是安裝目錄
* OPENCV_EXTRA_MODULES_PATH是加載額外模塊
* INSTALL_PYTHON_EXAMPLES是安裝官方python例程
* BUILD_EXAMPLES是編譯例程(這兩個可以不加,不加編譯稍微快一點點,想要C語言的例程的話,在最後一行前加參數INSTALL_C_EXAMPLES=ON,要C++例程的話在最後一行前加參數INSTALL_C_EXAMPLES=ONINSTALL_CXX_EXAMPLES=ON)
**/
 
sudo cmake -D CMAKE_BUILD_TYPE=RELEASE \
    -D CMAKE_INSTALL_PREFIX=/usr/local \
    -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib-3.4.3/modules \
    -D INSTALL_PYTHON_EXAMPLES=ON \
    -D INSTALL_CXX_EXAMPLES=ON \
    -D BUILD_EXAMPLES=ON ..
 

   

 

     配置完后如下圖:

   

 

  • 備份build文件中的東西

     因為下一步的編譯會使用build文件中的東西,假如編譯失敗后還要重新進行cmake,比較耽誤時間,這裏可以直接備份一下cmake好的build文件夾,

  命名為build1,重新make的時候可以拿來用。

1 //返回上層目錄
2 cd ..
3 //備份release文件夾
4 cp -r release ./release1

  

  • 為樹莓派增加SWAP

    在開始編譯之前,建議你增加交換空間。這將使你使用樹莓派的所有四個內核來編譯OpenCV,而不會由於內存耗盡導致編譯掛起。

    打開 etc dphys – swapfile   文件:

1 $ sudo nano /etc/dphys-swapfile

    然後編輯 CONF_SWAPSIZE  變量:

   

 

      注意:此處我將交換空間從100MB增加到2048MB;如果你不執行此步驟,你的樹莓派編譯時很可能掛起。

    重新啟動交換服務:

1 $ sudo /etc/init.d/dphys-swapfile stop
2 $ sudo /etc/init.d/dphys-swapfile start

   

 

     注意:增加交換空間的大小是燒壞樹莓派 microSD卡的好方法。基於閃存的存儲只能執行有限數量的寫操作,直到該卡基本不能夠容納1和0。我們只能在短時間內

  啟動大型交換,所以這沒什麼大問題的。

  • 編譯OpenCV 3

1 /**
2 * 以管理員身份,否則容易出錯
3 * make命令參數-j4指允許make使用最多4個線程進行編譯,這樣編譯速度會更快
4 * 可以根據自己機器的情況進行更改
5 * 使用tee命令可以將編譯過程中終端显示的信息保存到make.log文件中,便於查看,這樣即使VNC斷線,終端的* 信息太多看不到,也可以通過make.log文件查看編譯過程。
6 **/
7  
8 sudo make -j4 2>&1 | tee make.log

   

 

    如果看到進度編譯到100%,那麼說明編譯安裝成功。

   

1 // 安裝
2 sudo make install
3  
4 // 更新動態鏈接庫
5 sudo ldconfig

    

    注意: 不要忘記回到 /etc/dphysswapfile 文件:

       1.將 CONF_SWAPSIZE 重置為 100MB

       2.重新啟動交換服務

其他配置

   設置庫的路徑,相當於windows下的環境變量,便於使用OpenCV庫,也可以不進行設置,使用的時候說明路徑也可。例如在編譯時說明使用庫的路徑是 -L/usr/local/lib 

 

  1.配置opencv.conf 

   打開opencv.conf配置文件,在末端加入如下內容: 

 1 //這裏我使用的是樹莓派默認的nano,也可以使用vim、gedit.打開opencv.conf文件
 2 sudo nano /etc/ld.so.conf.d/opencv.conf
 3 
 4 /**
 5 *在末端添加如下內容
 6 *注意:?表示一個空格,可能原因是有的語言要求最後有一個空格才可以編譯通過。
 7 **/
 8 /usr/local/lib
 9 ?
10 
11 //加載一下
12sudo ldconfig

   

  2.打開 bash.bashrc 配置文件 

1 //打開bash.bashrc配置文件
2 sudo gedit /etc/bash.bashrc
3 
4 
5 // 在最後添加如下內容
6 PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig       
7 export PKG_CONFIG_PATH

    

  3.重啟樹莓派

1 sudo reboot

 

 檢測OpenCV使用是否正常

  python程序

 1 import cv2
 2 import numpy as np
 3 cv2.namedWindow("gray")
 4 img = np.zeros((512,512),np.uint8)#生成一張空的灰度圖像
 5 cv2.line(img,(0,0),(511,511),255,5)#繪製一條白色直線
 6 cv2.imshow("gray",img)#显示圖像
 7 #循環等待,按q鍵退出
 8 while True:
 9     key=cv2.waitKey(1)
10     if key==ord("q"):
11         break
12 cv2.destoryWindow("gray")

  保存文件為 test.py ,並在終端運行程序

1 sudo python3 test.py

  運行結果如下:

  

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

心裏有紅黑樹

Why 紅黑樹

為什麼大家都這麼推崇紅黑樹呢? 這就是數據結構的魅力!!! 下面我簡述一下常用數據結構的優缺點

  • 數組

大家對數組很熟悉, 都知道對數組來說,它底層的存儲空間是連續的,因此如果我們根據index去獲取元素,速度是相當快, 但是對於數組來說有時候查詢也不見得就一定塊, 比如我們查詢數組中名字叫張三的人, 也不得不從開始遍歷這個數組

如果我們想往數組中插入一個元素, 也不見得一定就慢, 比如我們往數組中最後的位置插入就很快, 但是要是往開始的位置插入的話, 肯定會很慢, 需要將現有數組中所有的元素往後移動一位, 才能空出開始的位置,給新元素用

  • 鏈表

一說鏈式存儲, 大家也都知道, 這種數據結構僅僅是邏輯連續, 物理存儲不連續, 因此我們有可以通過玩指針或者引用很快的完成元素的刪除和添加

對鏈表的查詢來說, 一定是慢的, 無論查詢誰, 查哪個, 都得從第一個節點開始遍歷

  • AVL樹

AVL樹, 就是二叉平衡樹, 這種有序的樹形結果就將鏈式存儲添加刪除塊, 順序存儲的查找快兩大有點進行了一次中和, 在絕大部分情況下, AVL樹在增刪改查方面的性能都原超過數組和鏈表

  • 紅黑樹

紅黑樹是對AVL樹是又一次重大升級, AVL樹,對於樹的平衡要求太嚴格了, 每當添加,刪除節點時,都不得不進行調整

對於AVL樹個紅黑樹來說, 每次添加一個新的節點都是最多進行兩次旋轉(左旋右旋)就能重新使樹變的平衡,

但是當我們刪除一個恭弘=叶 恭弘子節點時, AVL樹重新調整成平衡狀態時最多需要進行旋轉O(logN)次, 而紅黑樹最多旋轉3次就能重新平衡,時間複雜度是O(1)

還有就是紅黑樹並不是完全意義上的AVL樹, 也就是說它其實並不是真的像AVL樹那樣嚴格要求對一個節點來說左右子樹的高度差不能超過1, 而是選擇使用染成紅色和黑色進行維護

簡單來說, 因為紅黑樹並不像AVL樹那樣完全平衡, 可能會導致紅黑樹的讀性能略遜於AVL, 但是紅黑樹的維護成本絕對是遠遠低於AVL, 在空間上的開銷和AVL樹基本持平, 因此紅黑樹被大家極力推崇, 和學習java的同學直接相關的就是jdk8的 hashmap

紅黑樹的特性

紅黑樹主要存在下面的7條性質

  1. 節點非紅即黑
  2. 根節點必定是黑色
  3. 恭弘=叶 恭弘子節點全部是黑色, (這裏說的恭弘=叶 恭弘子節點是我們想象在肉眼看到的節點上再多加一層子節點)
  4. 紅節點的子節點必定是黑色
  5. 紅節點的父節點必定是黑色
  6. 從根節點到任意子節點的路徑上,都要經歷相同數目的黑節點
  7. 從根節點到任意子節點的路徑上不可能存在兩個連續相同的紅節點

常見的誤區

如上圖, 看着挺像紅黑樹, 其實他不是, 看它node10, 並不滿足上面的性質6. 因為我們認為node10的左子節點是黑色的節點, 這樣的話, 從node20到node10的左子節點就經歷了兩個黑節點, 而其他的 node15, node25, node35 經歷的黑色子節點數都是三個

如上圖它也不是紅黑樹, 因為我們認為node30的右節點是黑色的節點, 這樣的話從node60到node30的右節點就經歷了三個黑色的節點, 而其他的所有子節點都經歷了4個, 故, 他不是紅黑樹

紅黑樹與2-3-4樹等價

如上圖中,當我們將一個紅黑樹中的黑色節點和紅色節點融合在一起時,我們會發現, 這個紅黑樹其實就是一顆2-3-4樹, 一顆四階B樹

並且, 紅黑樹中黑色節點的每一個合併完成后的節點中都有一個黑色的節點, 換句話說就是紅黑樹中黑色節點的個數等於2-3-4樹中節點的個數

添加

添加節點其實就是構造紅黑樹的過程, 只要我們嚴格遵循上面的7條限制, 構造出來的樹就是紅黑樹

通過上圖其實我們發現, 紅黑樹真的可以和四階B樹之間進行等價代換, 換句話說就是 4階B樹的性質對於紅黑樹來書其實也是存在的, 主要是如下兩條性質

  • 所有新添加進去的節點都被放在了恭弘=叶 恭弘子節點上
  • 2-3-4樹中每一個節點中允許承載的元素的個數 [1,3]

經驗推薦: 就是新添加的節點盡量全部是紅色, 如果你畫一畫就會發現, 如果我們新添加的節點是紅色的話,上面所說的7條性質中, 除了第四條(紅節點的子節點必定的黑節點). 其他的限制都可以滿足

於是看一下一顆四階B樹插入節點時有哪些種情況

數一數: 一共 4+3+3+2 = 12種情況, 換句話說, 只要我們處理好了這12種情況, 我們就完成了添加節點的邏輯

  • 情況1, 就是假設我們添加進去的是紅色的節點, 並且這個紅色節點的父節點是黑色節點時, 直接添加進行,不需要其他任何變換, 就想下圖這樣, 直接簡單粗暴的添加就行

除去第一種情況外, 還剩下8中情況出現了紅紅節點相鄰, 於是繼續往下看, 我們對他進行一次修復

  • 情況2: 如下圖

插入的node57, node64, 什麼情況呢? 就是當前節點是node5556, 首先這個節點中現存兩個元素, 並且是往這個黑色的節點的左側的左側插入, 或者是右側的右側插入一個紅色節點

看上圖出現了兩個紅色節點相鄰,於是我們第一件事就是進行重新染色,

  1. 將插入節點的父節點染成黑色
  2. 將插入節點的祖父節點染成紅色
  3. 將祖父節點進行旋轉, 如果這個新節點被插入在父節點的右側. 左旋轉它的祖父節點

經過上面的變換后, 我們重新得到標準的紅黑樹如下

  • 情況3: 新添加的節點的叔叔節點不是紅色

第三種情況和第二種情況相似, 還是插入 node57和node64. 判斷的條件是 插入節點的叔叔節點(父節點的兄弟節點)不是紅節點,

簡稱 LR , 或者是RL , 需要進行如下的調整

  1. 染色: 將自己染成黑色,祖節點染成紅色
  2. LR: 父節點左旋轉, 祖父節點右旋轉
  3. RL: 祖父點右旋轉, 父節點左旋轉

LR舉例:

經過上面的變化,我們重新得到平衡的紅黑樹

接着往下看剩下的四種情況

  • 情況4: 新添加的節點的叔叔節點是紅色, 其實就是需要上溢的情況, 也很好處理

像上圖這樣, 新添加的紅色節點 node15, 它本身的父節點是node20, 父節點的叔叔節點是紅色的node25, 我們比較node15和node20的大小, 發現node15本來是應該放在node20的左邊的, 但是對於一顆2-3-4樹來說, 單個節點最多就有3個元素, 如果再加上node15 就會出現上溢的情況, 怎麼辦呢? 我們上溢調整, 選擇這個節點中間位置的元素向上和父節點合併, 選擇node20, node30其實都是可以的, 為了方便我們選擇node30

好,下面開始修復這個紅黑樹

  1. 將插入的節點的父節點和它的叔叔節點染成黑色
  2. 發生了上溢, 將他的父節點的染成紅色, 遞歸插入到根節點上, 這時候根節點可能又會發生上溢

然後上溢

當我們將新插入的節點的父節點node30染成紅色時, 再插入到根節點, 實際上就是重複我們枚舉出來的這12種情況中的一種. 紅黑樹一定會被修復, 當然這時候很可能會出現根節點也容納不了新的元素, 需要根節點也進行上溢, 然後將根節點染黑

還有一種情況是像下面這樣, 同樣是在情況4下的新插入的節點的叔叔節點是紅色

像下面這樣調整:

  1. 將父節點和叔叔節點染成黑色
  2. 祖父節點上溢

然後就是這種情況

調整的思路和前面一樣

  1. 將父節點和叔叔節點染成黑色
  2. 將祖父節點上溢

至此紅黑樹的添加的12種情況就全部枚舉完成了

刪除

對於刪除來說總共兩大種四小種情況

  • 第一種就是刪除的節點就是紅色節點, 如果真是這樣的話,直接刪除就ok
  • 第二種是刪除的節點是黑色節點
    • 刪除擁有1個red節點的黑色節點
    • 刪除擁有2個red節點的黑色節點,
    • 刪除黑色節點

如果一個像下面這樣, 下面的黑色節點有兩個子節點, 這種情況下,黑色節點肯定不會直接被刪除的, 需要進行變換,讓他的恭弘=叶 恭弘子節點去替換他,進而實現刪除的目的

  • 情況1: 刪除擁有1個紅節點的黑色節點,像下圖這樣

怎麼判斷這就是我們想刪除的情況呢? 當我們確定用來替代這個被刪除的黑節點是紅色,則符合當前的情況

也就是說我們想刪除 node40 和 node70, 於是我們這樣做

  1. 讓這個指向被刪除的節點的指針指向這個被刪除的節點的子節點
  2. 將替代它的節點染成黑色

於是我們接得到下圖這樣的結果

  • 情況2: 刪除的節點是黑色的恭弘=叶 恭弘子節點, 並且可向兄弟節點借

首先,如果這個恭弘=叶 恭弘子節點就是根節點的話,直接刪除就ok

看下面的這個圖, 我們就刪除其中node90, 即,刪除黑色恭弘=叶 恭弘子節點

如果想刪除上圖中的node90也是由竅門的,規律和2-3-4樹是擦不多的

假設它就是2-3-4樹, 如果我們將node90刪了, 我們計算一下, 對於2-3-4樹來說, 每一個節點位置上至少有 ⌈ 4/2 ⌉ -1 = 1個元素, 但是把node90刪除了這個位置上的節點中沒有元素, 因此產生了 下溢

出現下溢,我們首先考慮的情況就是看看可不可以向它的兄弟節點借一個,但是和B樹是有取別的, 多了下面的限制

  1. 被刪除的這個節點的兄弟節點必須是黑色的
  2. 被刪除的這個節點的兄弟節點一定的有紅色的子節點才ok, 就像上圖那樣, 可以在左邊,右邊,或者都有
  3. 直接刪除掉指定的node(因為它在恭弘=叶 恭弘子節點的位置上)
  4. 進行旋轉,旋轉時注意, 兩點:第一點: 比如下面的原來根節點位置上的元素88是紅色的, 經過旋轉上來替換它的節點的顏色必須染成紅色, 如果node88是黑色, 那麼經過旋轉上來替換他的節點的顏色必須染成黑色 ,第二點: 旋轉完成后,新的跟節點的直接左右子節點的顏色轉換為黑色

怎麼進行旋轉呢? 就像下圖這樣

  • 情況3: 刪除的節點是黑色的恭弘=叶 恭弘子節點, 並且它的兄弟是黑色,而且它的兄弟節點不能借給他元素

像這種情況:我像刪除node99,但是沒辦法像他的兄弟節點借元素,於是

  1. 將父節點向下合併,父節點染成黑色
  2. 將它的兄弟節點染成紅色

也有特殊的情況, 就是它的父節點只有一個,還是黑色

這時候,我們將他的父節點下溢, 原位置的節點捨棄

  • 還有最後一種情況就是, 刪除的是黑色的節點, 它的兄弟節點的是紅色的節點

就像上圖那樣,我們想刪除node99, 但是node99的兄弟節點其實是node55, 而不是node77, 我們怎麼樣才能轉換為前面說的那些情況呢?

  1. 將被刪除節點的父節點染成紅色, 兄弟節點染黑

  2. 讓被刪除的父節點進行右旋轉(node88右轉)

    得到下圖

於是我們就將這種兄弟節點為紅節點的情況轉化成了兄弟節點為黑色節點的樣子, 按照原來的方式進行刪除修整即可

  1. 讓原父節點下溢
  2. 原染成黑色
  3. 兄弟節點,染成紅色

至此本文就結束, 歡迎關注我,後續我更新更多的關於開發相關的筆記

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

法國要在2040 年停售汽柴油車,能源部長:這會是一場革命

不同於美國退出巴黎協定的決定,法國總統馬克宏曾提到要「讓地球再次偉大」,如今看來,法國似乎正朝目標一步步邁進中。法國能源部長余洛(Nicolas Hulot)6 日宣布,將在2040 年開始全面停售汽柴油車。

電訊報報導,除了汽柴油車之外,法國同時也將禁止任何使用汽油、煤氣、煤炭及頁岩油的新計畫,余洛表示,汽油及汽車的銷售將在2040 年前結束,「法國打算在2050 年達到碳中和的目標。」

為了達成這個目標,法國將開始進行必要的投資,並提供相關獎勵補助措施,這個目標是非常困難的,特別是對於車商來說,但余洛相信汽車產業已經準備好進行轉變。

根據歐洲經濟區(EEA)的統計,在2016 年大型車商的汽車碳排放量上,法國車廠Peugeot、Citroen、Renault 是碳排放最低的前三名。

儘管如此,這樣的改變勢必會面臨許多挑戰,余洛表示,「這會是一場名副其實的革命。」

余洛也引述了Volvo 近期的決定,來說明停售汽柴油車的目標已是勢在必行。

就在幾天前,傳統車商Volvo 宣布將於2019 年起生產電動車,並停售純內燃機供電的汽車,執行長表示,Volvo 是考量過客戶需求,才做出這項決定。

法國並不是唯一將開始禁止汽柴油車的國家,德國和印度都打算在2030 年前達到這個目標,荷蘭和挪威更計畫在2025 年前達成。

根據統計,2017 上半年在法國的新車市場中,使用柴油和汽油的車輛仍佔有95.2%,混合動力車佔3.5%,純電動車僅佔了1.2%。

余洛強調,儘管法國在這方面,落後於瑞典、哥斯大黎加等國家,但會擁抱綠能的「精神」,期望在2040 年前能達成目標。

除了停售汽柴油車及停止汽油相關新項目外,法國還計畫在2022 年將火力發電廠停工,並在2025 年將核電的供電比例由現在的75% 降低至50%,同時將採取措施,限制棕櫚油在生物燃料中的使用,來間接減少砍伐森林。

不僅是中國和印度,在法國的一些城市,巴黎、里昂和格勒諾布爾(Grenoble),也都存在著霧霾的問題。

「解決問題的方法就在可見之處,國內的製造商有著實現這項承諾的手段,這關係到公共衛生。」

(合作媒體:。圖片出處:wikipedia CC0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

另類思考!電動車熱未必有害原油、說不定恰恰相反?

國際車廠爭相研發電動車,汽油車似乎被宣判死刑,原油也會跟著步上末日。不過有專家大膽推估,電動車發展會削減流入原油業的資金,油市將汰弱留強,油價會維持低檔,也許將阻礙電動車普及之路。

加拿大能源私募基金龍頭ARC Financial Corp.首席能源經濟學家Peter Tertzakian,13日在OilPrice發文稱,上週Volvo宣布,2019年起只生產電動車、停產汽柴油車。法國政府也宣布,2040年起停售汽柴油車。與此同時,特斯拉創辦人馬斯克貼出平價電動車Model 3照片,讓油市陷入低迷。

電動車發展迅速,不少人認為原油業末日倒數計時。但是Tertzakian不以為然,指出環保車買氣疲弱。2013~2014年中,油價處於每桶100美元的高點時,美國電動車銷售提高,可是之後油價崩盤,電動車動能隨之消散。能源有兩股相反力量,現在的消費趨勢是原油便宜、使用率高,相對的,電動車滲透率低迷。但是市場對未來的預期恰恰相反,斷言原油將亡。

未來幾年究竟會如何發展,Tertzakian推測有兩種可能結果,一是資本緊縮,汰除沒有效率的生產商,剩餘業者將不斷創新壓低成本,在低油價時也能存活,油價會繼續低迷,消費者缺乏誘因改買電動車。第二種可能是資本投資緊縮,將使原油供給下降,帶動油價上揚,電動車需求因此大增。諷刺的是,厲害的油商在兩種情況下,都能有好表現。可以確定的是,未來投入原油業的資金勢必減少。

原油期貨報價從去(2016)年12月開始一路往下盤跌,高盛全球原物料研究部主管Jeffrey Currie對這提出了一個簡單解釋:市場上的錢太多。

CNBC報導,Currie 12日在接受專訪時指出,市場上的原油其實並不是太多,只是錢潮淹腳目、委實過多。舉例來說,人們對石油探鑽活動的投資,是墨西哥最近能發現十數億桶原油的主因。

Currie說,石油市場正在試著尋找供需均衡點,而產油商的損益兩平點在跌破高盛2016年估計的50-55美元後,新的平衡點在哪,則變得愈來愈難預測。不過,在被問到油市有沒有可能崩盤時,Currie說雖然他希望看到市場能有些波動,但其實崩盤機率極低。

(本文內容由授權使用。圖片出處:public domain CC0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!