2019年5月16日 星期四

AtomicXxx、ABA

※AtomicInteger


AtomicInteger ai = new AtomicInteger(); // 建構子可給初始值,不給預設為 0
System.out.println(ai.get()); //取得當前值 0
System.out.println(ai.getAndIncrement()); // 後置遞增 0
System.out.println(ai.incrementAndGet()); // 前置遞增 2
System.out.println(ai.getAndAdd(3)); // 2
System.out.println(ai.addAndGet(3)); // 8
System.out.println(ai.getAndSet(7)); // 8
System.out.println(ai.get()); // 7
ai.set(9); // 回傳 void
System.out.println(ai.get()); // 9

※increment、decrement 為加減1,想加減更多用 addAndGet、getAndSet

※AtomicXxx Xxx 為 Wrapper 類別,都類似這樣的用法



※AtomicIntegerArray

int[] ints = {1, 2};
AtomicIntegerArray arr = new AtomicIntegerArray(ints);
arr.getAndSet(0, 9); // 第0個改成9
System.out.println(arr.get(0));//成功修改
System.out.println(ints[0]); // 原本的陣列並不會被改

※針對陣列的其中一個索引修改



※AtomicReference

MyClass c1 = new MyClass(1, "Tom");
MyClass c2 = new MyClass(2, "Jerry");
    
AtomicReference<MyClass> ref = new AtomicReference<>();
ref.set(c1);
ref.compareAndSet(c1, c2);
System.out.println(ref.get().getId());
System.out.println(ref.get().getName());
    
    
    
@Getter
@Setter
class MyClass {
    private int id;
    private String name;
    public MyClass(int id, String name){
        this.id = id;
        this.name = name;
    }
}

※針對整體物件的修改



※AtomicIntegerFieldUpdater

MyClass c1 = new MyClass(1, "Tom");
    
AtomicIntegerFieldUpdater<MyClass> atomic = AtomicIntegerFieldUpdater.newUpdater(MyClass.class, "id");
System.out.println(atomic.getAndIncrement(c1));
System.out.println(atomic.get(c1));
    
class MyClass {
    volatile int id;
    private String name;
    MyClass(int id, String name){
        this.id = id;
        this.name = name;
    }
}

※針對物件裡的 Integer 修改,當然替換 Integer 也有其他 Wrapper 類別

※注意 Myclass 的欄位必需不是 private 且為 volatile 才可以,會有錯誤提示



※AtomicReferenceFieldUpdater

MyClass c1 = new MyClass(1, "Tom");
    
AtomicReferenceFieldUpdater<MyClass, String> atomic = AtomicReferenceFieldUpdater.newUpdater(MyClass.class, String.class, "name");
atomic.set(c1, "Jerry");
// System.out.println(atomic.getAndSet(c1, "Jerry"));
// System.out.println(atomic.compareAndSet(c1, "Tom", "Jerry")); // 舊值如果不是 Tom 就不更新,回傳 boolean
System.out.println(atomic.get(c1));
    
    
    
class MyClass {
    private int id;
    volatile String name;
    MyClass(int id, String name){
        this.id = id;
        this.name = name;
    }
}

※針對物件裡的物件修改

※使用此方式更新的欄位,一定要使用 Wrapper 類別,如此例 id 不是 Integer 會報錯,必需將 MyClass 和 newUpdate 的第二個參數都使用 Integer 才行



※AtomicStampedReference

Integer i1 = 10;
AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(i1, 200);
System.out.println(atomic.compareAndSet(10, 11, 200, 201)); // true,預期舊值是 10 且 stamp 是 200 就更新值為11,新的 stamp 為201
System.out.println(i1); // 10
System.out.println(atomic.getReference()); // 11
System.out.println(atomic.getStamp()); // 201
System.out.println(atomic.get(new int[]{i1})); // 11

※針對 ABA 的問題,用此類可解決

※前兩個參數 -128~127 因有 cache,看起來不會有問題,但超過就不行了,永遠是false,就算 i1 改用 int 也會自動裝箱成 Integer
Byte、Short、Integer、Long、Character 都有這個問題,可看 XxxCache
後兩個參數沒有這個問題,因為底層是 int


.較好的寫法

AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(1000, 200);
System.out.println(atomic.compareAndSet(atomic.getReference(), 11, 200, 201)); // true,以 atomic 為主就是同個記憶體,這樣就不會有問題了
System.out.println(atomic.getReference()); // 11
System.out.println(atomic.getStamp()); // 201

※這樣子寫就不用管 -128~127 的問題了


.使用 AtomicStampedReference 針對整個物件修改

MyClass c1 = new MyClass(1, "Tom");
MyClass c2 = new MyClass(2, "Jerry");
//Integer i1 = 10;
AtomicStampedReference<MyClass> atomic = new AtomicStampedReference<>(c1, 200);
System.out.println(atomic.compareAndSet(atomic.getReference(), c2, 200, 201)); // true
MyClass ref = atomic.getReference();
System.out.println(ref.getId()); // 2
System.out.println(ref.getName()); // Jerry
System.out.println(atomic.getStamp()); // 201
    
    
    
@Getter
@Setter
class MyClass {
    private int id;
    private String name;
    
    MyClass(int id, String name) {
        this.id = id;
        this.name = name;
    }
}



2019年5月15日 星期三

docker

docker version     看版本,我的是18.09.2
docker info           比 version 多很多資訊

docker run hello-world 不加tag,预设为「:latest」
表示執行 hello-world 這個鏡像,有就執行;沒有就去下載並執行

docker search 鏡像名     搜尋鏡像
--no-trunc        不截斷描述
--limit(后接数字,预设为25)       只顯示多少個

docker pull 鏡像名:tag    下載鏡像 (tag不加一樣為 :latest )

tag 要到官網搜尋,以tomcat為例,如下:

每一個都是tag,所以下 docker pull tomcat:7.0.94-jre7 即可
如果 docker search 或 pull 太慢,Windows 10 可在右下叫出 docker 設定,並增加網址,如下:

按 Basic 有他的格式,和 linux 的 /etc/docker/daemon.json 裡一模一樣
再使用 docker info 時,接近最下面有 Registry Mirrors: 可看到,表示設定成功了

docker images 看目前有什麼鏡像
 --all             顯示所有鏡像
--quiet         只顯示 Image ID
--digests      顯示摘要
--no-trunc    不截斷 Image ID

docker rmi   移除鏡像,多個用空格格開
docker rmi --force $(docker images-qa)   一次刪除多個鏡像

docker run -it [imageID|imageName] /bin/bash
exit 離開 Ctrl+P+Q 暫時離開
--name 容器別名(不指定會隨機給一個)
-p      為 port 8888:8080,左邊為想要給的 port;右邊為程式的 port
-P      隨機 port
-d      背後執行,但如果沒有進程在跑,會立即關閉
docker run -d imageID /bin/sh -c "while true; do echo hello ooo; sleep 2; done"

docker ps  顯示正在執行的容器
--all       顯示全部容器,包括執行中,已關閉
--latest  顯示最新的容器
--last 次數  或者 -n 次數    顯示最後幾個容器
--quiet     只顯示容器編號
--size   顯示總大小

docker start 容器名 啟動曾經啟動過的容器
docker restart 容器名
docker stop 溫柔關閉容器
docker kill 強制關閉容器
docker rm 刪除容器 -f 刪還沒啟動的
docker rm --force $(docker ps --all --quiet)
docker ps -all -quiet | xargs docker rm
docker logs --follow -timestamps --tail 容器 id
docker top 容器id      看運行的進程
docker inspect 容器名:看容器內的細節
docker attach 重新進入container
docker exec --tty 容器id linux命令, 加 -tty 會顯示的比較好看,不一樣要有
docker exec /bin/bash 同attach
docker cp 容器id:路徑 本機路徑
docker commit --message=訊息 --author=作者 容器id 命名空間/自定義名稱:tag
因為鏡像一關閉就一切還原了,所以使用 commit 可新增一個自己的鏡像
除了可以使用 commit 外,還可以使用 run 的 -v 參數和 docker file

-v參數:

docker run -it -v /hostFolder: /ContainerFolder imageName /bin/bash
docker run -it -v /hostFolder: /ContainerFolder:ro imageName /bin/bash 容器裡只讀
docker inspect
docker start containerId
ro 是 read Only的意思,不加預設是可讀寫
如果遇到權限問題,可加 --privileged=true 試試
在 Windows10 我沒試出來,都是格式不對的錯



docker file:

from ubuntu    -> ubuntu 為 image 名稱
VOLUME ["/container1", "/container2"]   -> 同 -v,根目錄建兩個資料夾來同步,匿名的方式
cmd echo "finish"
cmd /bin/bash

docker build --file /mydocker/Dockerfile --tag zzyy/ubuntu(namespace) .
如果檔名取 Dockerfile 可不加-f
在 Windows10 也是沒用

-t imageName 可以生成映象檔

RUN yum install -y vim 安裝 vi
RUN yum install -y net-tools 安裝網路相關

WORKDIR 一進入 linux 的路徑

此時主機要交互的資料夾要看 inspect 裡的 volumes

CMD ["ls", "-l"]
ENTRYPOINT ["ls", "-l"]
都是執行命令,但 CMD 是執行而已;而 ENTRYPOINT 是追加
docker run 容器ID -l 可以看出差別

docker run -it --name 子容器名 --volumes-from 父容器名 namespace

https://docs.docker.com/engine/reference/builder/