2017年3月22日 星期三

聚合、依賴、繼承、插件 (Maven 4)

※聚合


上面的 A、B 專案,都要先 install 才能讓 C 抓到,這如果不只這兩個專案,那每一個都要先 install,太沒效率了,所以可用繼承的方式,統一用一個 pom.xml 就好了,操作如下:

1.新增 maven 專案後,勾下面的選項



 2.package 要選 pom



※D的pom


<project ...>
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>maven_test</groupId>
    <artifactId>D</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    
    <modules>
        <module>../A</module>
        <module>../B</module>
        <module>../C</module>
    </modules>
</project>

※因為 4 個專案同一層,所以要到上一層才可以抓到其他專案

※packaging 裡的 pom 一定要小寫

※此時只要操作 D 專案即可一次統一操作 3 個專案

※但有些人不想要新增一個專案,也可以將 D 的 pom.xml 複製到 ABC 三個專案的同層,而 D 專案也刪了,最後用命令提示字元下 mvn clean install,當然 Eclipse 還是能 import



※依賴


※左圖

C 專案依賴 A 和 B 專案,只要 scope 是 compile(預設) ,就會將 A、B 兩個專案的 jar 檔,複製到 C 專案
如果有同樣的 jar 檔,會以依賴最短的優先,但路徑都是 1,所以就會以 pom 檔的順序為主,誰先寫誰就贏了

※右圖

C 專案依賴 B 和 A1 專案,只要 scope 是 compile(預設) ,就會將 B、A1 兩個專案的 jar 檔,複製到 C 專案,但 A1 又依賴 A 專案,所以 A 也會複製給 C 專案
如果有同樣的 jar 檔,會以依賴最短的優先,所以會以 B 為主

如果不想依賴 maven 規定好的 jar,那就可以在不想依賴的 <dependency>增加<exclusions>來排除
<dependency>
    <groupId>maven_test</groupId>
    <artifactId>C</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>


※注意 C 專案要抓到 A、B 專案,必需先 mvn install ,這樣 .m2 才會有 jar 檔供 C 下載



※繼承

※父類

<groupId>bruce.chen</groupId>
<artifactId>D</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
    
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.2.2.RELEASE</version>
        </dependency>
    </dependencies>
</dependencyManagement>

※packaging 一定是 pom,否則子類會報錯

※使用 dependencyManagement 標籤將 dependencies 裝起來

※子類

<artifactId>B</artifactId>
<packaging>jar</packaging>
    
<parent>
    <groupId>bruce.chen</groupId>
    <artifactId>D</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>
    
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>


※寫個 parent 標籤,表示它還有父 pom

※自己坐標的 groupId 和 version 如果和父類一樣,也可以刪除

※dependency 標籤的 version 也可以刪除

※parent 有個子標籤 relativePath,預設是 ../pom.xml,如果不是就要打出來了


※插件

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<phase>generate-test-resources</phase>
<goals>
<goal>jar</goal>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

phase 要打上生命週期的名字,也就是哪時候執行這個插件
goal 可以多個,此例是打原碼包和打測試包,至於裡面要打什麼,可參考下圖


左邊選擇 Maven Plugins 會自動跳動上圖的 Plugins,然後選擇 source,表示要打源碼包,點進去就會看到可以用的 goals

此例在 compile phase 並不會打上面設定的 jar 包,可參考下圖



可以看出 compile 還沒到我們指定的 phase,但 package 有包括,加上原本的 jar,總共有三個
demo-0.0.1-SNAPSHOT.jar:原本的 jar 包
demo-0.0.1-SNAPSHOT-sources.jar:goal 為 jar 生出來的,內容和原本的一模一樣,不會有測試的類別
demo-0.0.1-SNAPSHOT-test-sources.jar:goal 為 test-jar 生出來的, 內容只有測試類別
.可以用解壓工具看裡面的內容
.如果專案是 springboot,還會有第四包,springboot 是可以執行的 jar 包,但要打上如下的標籤
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
通常 IDE 內鍵都會幫忙生成

2017年3月21日 星期二

The import XXX conflicts with a type defined in the same file

The import XXX conflicts with a type defined in the same file
假設新增一個 java 檔,檔名叫 List,那麼使用到 java 內鍵的List時,假設是 java.util.List 時,import 時就會出現這個錯
解決方法是使用package.class名稱,就不要 import 了,如java.util.List<String> list = new ArrayList<>();

假設檔名叫 Test.java,那麼使用 junit 的 @Test 時也要全部打,不要 import 了,如 @org.junit.Test

以上就算自己新建的檔案有 package 名稱,一樣會出這個錯

2017年3月15日 星期三

增加ORCL

做完此篇的動作,即可使用PL/SQL Developer

安裝完 Oracle 後,ORCL 的路徑在 app\BAU\product\11.2.0\dbhome_1\NETWORK\ADMIN\tnsnames.ora
想增加可用Net Manager


1.在程式集可看到如下的畫面
 
2.選到Service Naming 後,按旁邊綠色的加號,名字是待會在PL/SQL Developer 登入時的 Database 欄位會出現的 (做完後Service Naming下也會出現)


3.選預設的即可


4.這IP要和專案的人要


5.Service Name 也是要和專案的人要,如 192.168.11.22:1521:xxx 裡的xxx


6.不放心可按 Test... 測試一下,有自信就直接按 Finish 了


7.如果第 6 步是選 Test...,就會看到這個畫面,按 Change Login...,可用帳號密碼測試


按下完成後,會在app\BAU\product\11.2.0\dbhome_1\NETWORK\ADMIN\tnsnames.ora 裡的內容發現已經增加了

Linux 的連結和 Windows 的 mklink

不管是Linux 還 Windows ,連結檔的容量都是很小的

Linux

ln [-s] 已有的檔案或目錄名稱 檔案或目錄名稱

硬連結

    語法:ln <目的> <來源>
    1.將一個檔案硬連結和多個檔案以後,假設是5個,刪除其中1~4個不會影響本來的檔案
    2.只能是同一個檔案系統
    3.inode 一樣
    4.只能操作檔案
    5.ls -l 查看時,第一個並不是L,是-

軟連結

    語法:ln -s <目的> <來源>
    1.目的檔案若被刪除,則來源會出現錯誤
    2.可跨越不同的檔案系統
    3.inode 不同
    4.ls -l 查看時,是 L
    5.-s = --symbolic

還有一個參數 f,意思是如果目的檔已經有了,會先刪除再新增



Windows

以下是用Windows 7 的實驗,必需有管理的權限來操作,而且來源的檔案或目錄不可存在
mklink [/H /S /J] 檔案或目錄名稱 已有的檔案或目錄名稱

先切換到想連結的地方,然後如下操作

硬連結(Hard)

    語法:mklink -h <來源> <目的>
    1.只對檔案有效,如果目的為目錄,會出現「存取被拒」
    2.也只能在同個槽才行,如 C 槽不能到 D 槽

軟連結(Symbolic/Soft)

    語法:
        針對檔案:mklink <來源> <目的>,如果目的為目錄會成功,但沒作用,無意義
        針對目錄:mklink -d <來源> <目的>,如果目的為檔案會成功,但點下去會報錯,無意義

NTFS 連結(Junction)

    故名思義,檔案系統是NTFS才可使用,在X槽按右鍵-->屬性的一般活頁標籤可看到
    但只能使用在目錄
    語法:mklink -j <來源> <目的>,目的為檔案會成功,但點下去會報錯,無意義

軟連結的目錄和 NTFS 連結的差異

1.右鍵屬性時,軟連結會多一個 Shortcut 的活頁標籤

2.使用 dir 指令時,軟連結是<SYMLINK>,NTFS 連結是<JUNCTION>

3.複製時不同,將這兩個連結複製到其他的目錄後
    .軟連結只有原本的目錄才可以連
    .NTFS 連結無法複製,複製完後到目的資料夾貼上時,沒有作用

4.移動時不同,將這兩個連結剪下到其他的目錄貼上後
    .軟連結真的會移動,也可連結
    .NTFS 連結會複製一份到新的目錄,原本的還可以連結,但複製後的不行
    ※注意 NTFS 連結還會將原本目錄的所有檔案、目錄全部移動到新的目錄,造成原本的目錄 是空的情形,此時所有連結裡面都是空的



2017年3月8日 星期三

ExecutorService (Thread 六)

連線到資料庫有個連線池,Thread 也有 Thread pool ,ExecutorService 就是個 Thread pool


※取得ExecutorService

以下是overloading,以下只列出同名的其中一個方法


ExecutorService s1 = Executors.newCachedThreadPool();// 可快取的 Thread,預設idle 60 秒
ExecutorService s2 = Executors.newFixedThreadPool(3);// 固定數量的 Thread
ExecutorService s3 = Executors.newSingleThreadExecutor();// 單一的 Thread
    
ScheduledExecutorService sch1 = Executors.newScheduledThreadPool(3);// 可排程的 Thread
ScheduledExecutorService sch2 = Executors.newSingleThreadScheduledExecutor();// 單一可排程的

※最後兩個可排程,但要用 ScheduledExecutorService 去接

※Executors.newFixedThreadPool(1) 等於 Executors.newSingleThreadExecutor(),看原始碼就知道,排程的也是一樣



※ScheduledExecutorService

ScheduledExecutorService 有4個方法
delay配合TimeUnit的時間間隔,是設定時間用的

delay:多少時間後開始執行
<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

以下兩個不容易分
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
initialDelay:多少時間後開始執行,只有第一次
period、delay:第二次(含)之後看的是這個時間
period、delay不同的是
period:command 的開始時間開始算,算到 period 設定的時間為一個間隔,如果設定5秒就每5秒執行一次
delay:command 的結束時間開始算,算到 delay 設定的時間為一個間隔,如果設定5秒就每5秒加上 command 執行的時間執行一次

※排程範例

ScheduledExecutorService sch1 = Executors.newScheduledThreadPool(1);
ScheduledExecutorService sch2 = Executors.newSingleThreadScheduledExecutor();
    
ScheduledFuture<String> future1 = sch1.schedule(new Callable<String>() {
    public String call() throws Exception {
        return "xxx";
    }
}, 3, TimeUnit.SECONDS);
    
try {
    System.out.println(future1.get());
    sch1.shutdown();
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
    
ScheduledFuture<?> future2 = sch2.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        System.out.println("哇哈哈");
    }
}, 1, 1, TimeUnit.SECONDS);
    
try {
    future2.get();
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

※注意 future2 的順序,以上面的例子是先執行3秒後的xxx,然後再執行哇哈哈
如果將 future2 放到 future1 的下面,然後在 future1.get() 下增加 future2.get(),最下面的 future2.get() 刪除,此時結果會是1秒的哇哈哈先執行



※Future 的 cancel 方法

參數是布林值,表示如果任務正在執行,是否要取消
回傳是否成功取消,如果為false,通常已經正常完成


ScheduledExecutorService sch = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> future = sch.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        System.out.println("哇哈哈");
    }
}, 1, 1, TimeUnit.SECONDS);
    
ScheduledFuture<?> f = sch.schedule(new Callable<Boolean>() {
    public Boolean call() throws Exception {
        return future.cancel(true);
    }
}, 5, TimeUnit.SECONDS);
    
try {
    // System.out.println(future.get());
    System.out.println("是否取消=" + f.get());
    sch.shutdown();
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

※註解打開會出「java.util.concurrent.CancellationException」的錯


2017年3月6日 星期一

集合、ThreadLocal (Thread 五)

※集合

System.out.println("synchronized list");
List<Integer> l = new ArrayList<>();
synchronized (l) {
    l.add(1);
    l.add(2);
    l.add(3);
}
for (Integer i : l) {
    System.out.println(i);
}
    
System.out.println();
System.out.println("synchronizedCollection");
Collection<String> c = Collections.synchronizedCollection(new ArrayList<>());
c.add("A");
c.add("B");
c.add("C");
synchronized (c) {
    for (String s : c) {
        System.out.println(s);
    }
}
    
System.out.println();
System.out.println("synchronizedList");
List<String> list = Collections.synchronizedList(new ArrayList<>());
list.add("D");
list.add("E");
list.add("F");
    
synchronized (list) {
    for (String s : list) {
        System.out.println(s);
    }
}
    
System.out.println();
System.out.println("synchronizedSet");
Set<String> set = Collections.synchronizedSet(new HashSet<>());
set.add("G");
set.add("H");
set.add("G");
synchronized (set) {
    for (String s : set) {
        System.out.println(s);
    }
}
    
System.out.println();
System.out.println("synchronizedMap");
Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>());
map.put(0, "A");
map.put(1, "B");
map.put(2, "C");
for (int i = 0; i < map.size(); i++) {
    System.out.println(map.get(i));
}

※這些是 java 1.5 之前的寫法,for 迴圈有用 synchronized ,是因為使用到 Iterator ,它並不是 Thread-safe,而 for each 會用到 Iterator 物件,但最原始的 for 迴圈不會用到



※ java 1.5 集合

System.out.println("CopyOnWriteArrayList");
List<String> list = new CopyOnWriteArrayList<>();
list.add("D");
list.add("E");
list.add("F");
for (String s : list) {
    System.out.println(s);
}
    
System.out.println();
System.out.println("CopyOnWriteArraySet");
Set<String> set = new CopyOnWriteArraySet<>();
set.add("G");
set.add("H");
set.add("G");
for (String s : set) {
    System.out.println(s);
}
    
System.out.println();
System.out.println("ConcurrentHashMap");
Map<Integer, String> map = new ConcurrentHashMap<>();
map.put(0, "A");
map.put(1, "B");
map.put(2, "C");
for (int i = 0; i < map.size(); i++) {
    System.out.println(map.get(i));
}

※java 1.5 新增了 java.util.concurrent 這個套件



※ThreadLocal

ThreadLocal 是 Thread 變數,在寫 Thread-safe 是很困難的,要讓 Thread 共用,又要同步,又有效能的問題,還要小心 Dead Lock,所以有人想到不要共用了,直接創造 Thread 的複本,在 java 1.2 就產生了 ThreadLocal 類別了,但此方法會需要較多的記憶體


public class ThreadTest extends Thread {
    private static int number;
    
    public int getNumber() {
        number = number + 1;
        return number;
    }
    
    // private static ThreadLocal<Integer> number = new ThreadLocal<Integer>() {
    // @Override
    // protected Integer initialValue() {
    // return 0;
    // }
    // };
    
    // public int getNumber() {
    // number.set(number.get() + 1);
    // return number.get();
    // }
    
    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(Thread.currentThread().getName() + "=" + getNumber());
        }
    }
    
    public static void main(String... a) {
        ThreadTest t1 = new ThreadTest();
        ThreadTest t2 = new ThreadTest();
        ThreadTest t3 = new ThreadTest();
    
        t1.start();
        t2.start();
        t3.start();
    }
}

※結果:
Thread-0=1
Thread-0=4
Thread-0=5
Thread-1=3
Thread-1=6
Thread-1=7
Thread-2=2
Thread-2=8
Thread-2=9

可以看得出來 Thread 之間被影響了,應該三個 Thread 都是 123 才是正確的


※打開註解並註解原來的 number 和 getNumber(),結果如下:
Thread-0=1
Thread-0=2
Thread-0=3
Thread-2=1
Thread-2=2
Thread-2=3
Thread-1=1
Thread-1=2
Thread-1=3


synchronized、volatile、wait、notify、notifyAll (Thread 四)

synchronized 是同步的意思,其中一個 Thread 執行後,其他的 Thread 也要知道,所以在聲明的區塊裡,一次只能有一個 Thread 執行 (static 除外) ,可以寫在方法、區塊、static method 裡,有四種方式

volatile

synchronized 只能寫在方法,而 volatile 只能寫在變數
使用 volatile 宣告的變數,會在記憶體裡取値;沒宣告會在 cache 取值,所以宣告 volatile 後會比較慢
但沒宣告 volatile 有可能記憶體裡的值已被更改,造成資料不同步,所以非必要不要加
在兩個以上 (含) 的 Thread 會使用到的成員變數可以加 volatile,但如果成員變數已在 synchronized 裡或有 final 關鍵字,就不必加了


※synchronized

class ATM implements Runnable {
    private Account a;
    private Integer money;
    
    public ATM(Account a, Integer money) {
        this.a = a;
        this.money = money;
    }
    
    @Override
    public void run() {
        a.executeWithDraw(a, money);
    }
}
    
class Account {
    private int totalMoney;
    
    public Account(int money) {
        this.totalMoney = money;
    }
    
    public int getTotalMoney() {
        return totalMoney;
    }
    
    public void executeWithDraw(Account a, int money) {
        synchronized (this) {
            System.out.print("帳號:" + Thread.currentThread().getName());
            int tmpTotalMoney = totalMoney;
            for (int i = 1; i <= 99999; i++);
    
            tmpTotalMoney -= money;
            if (tmpTotalMoney < 0) {
                System.out.println("$不夠");
            } else {
                totalMoney = tmpTotalMoney;
            }
    
            System.out.println("領" + money + "元, 剩" + a.getTotalMoney() + "元");
        }
    }
}

※for 迴圈模擬提款的時間

※synchronized 寫在方法和 synchronized() 差在,一個是整個方法都 synchronized;另一個是synchronized 區塊裡才是 synchronized,所以外面可以加一些和 synchronized 無關的程式碼,效能會比較好

※另外兩個synchronized 是 static 的,其中一個和寫在方法一樣,另一個是synchronized(Xxx.class)

※測試

Account a = new Account(10000);
System.out.println("目前共" + a.getTotalMoney() + "元");
Thread t1 = new Thread(new ATM(a, 5000));
Thread t2 = new Thread(new ATM(a, 2000));
Thread t3 = new Thread(new ATM(a, 4000));
t1.start();
t2.start();
t3.start();





※wait、notify、notifyAll


這三個方法都是 Object 的,都必需寫在 synchronized 裡,否則會出「java.lang.IllegalMonitorStateException」的錯
使用 wait 後,必需用 notify 或 notifyAll 喚醒,兩者差在一個呼叫一個 Thread;另一個呼叫全部的 Thread,呼叫哪一個或呼叫的 Thread 順序,是以 JVM 的算法為準,不是優先權值

wait 會釋放鎖,所以一執行完這一行,其他的 Thread 可以呼叫


public class ThreadTest extends Thread {
    @Override
    public void run() {
        synchronized (this) {
            System.out.println("call notify:" + Thread.currentThread());
            notify();
        }
    }
    
    public static void main(String... a) {
        ThreadTest t = new ThreadTest();
        t.start();
    
        synchronized (t) {
            System.out.println("call wail:" + Thread.currentThread());
            try {
                t.wait();
                System.out.println("...");
            } catch (InterruptedException e) {
                System.out.println("wait interrupted!");
            }
            System.out.println("......");
        }
    }
}

※結果:
call wail:Thread[main,5,main]
call notify:Thread[Thread-0,5,main]
...
......

※Thread.currentThread 印出的是「Thread 名稱、優先權值、Thread 群組名稱」


※以喝完水要加水為例

class Water {
    private static boolean empty = true;
    
    public synchronized void put(int i) {
        if (!empty) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("加第" + i + "次水");
        empty = false;
        notify();
    }
    
    public synchronized void eat(int i) {
        if (empty) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("喝第" + i + "次水");
        empty = true;
        notify();
    }
}
    
class Drink implements Runnable {
    Water w;
    
    Drink(Water c) {
        this.w = c;
    }
    
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            w.eat(i);
        }
    }
}
    
class Add implements Runnable {
    Water w;
    
    Add(Water c) {
        this.w = c;
    }
    
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            w.put(i);
        }
    }
}




※測試

Water c = new Water();
Thread p = new Thread(new Add(c));
Thread e = new Thread(new Drink(c));
e.start();
p.start();



2017年3月2日 星期四

優先權、join、yield、sleep、interrupt (Thread 三)

※優先權

public class ThreadTest extends Thread {
    @Override
    public void run() {
        System.out.println("name=" + Thread.currentThread().getName());
        System.out.println("priority=" + Thread.currentThread().getPriority());
    }
    
    public static void main(String... a) {
        Thread t = new Thread(new ThreadTest());
        t.start();
        t.setPriority(MAX_PRIORITY);
    }
}

※預設優先權為5,可設1~10的正整數,數字越大,優先權越高

※Thread 類別有三個field,MIN_PRIORITY、NORM__PRIORITY、MAX__PRIORITY,數字分別是 1、5、10,想設其他的就要自己寫死的了,但如果不在 1~10 裡,會出「java.lang.IllegalArgumentException」的錯



※join

哪一個 Thread 使用 join ,那就會先執行完自己的Thread,結束後才會執行其他的 Thread


public class ThreadTest extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 50; i++) {
            System.out.println("join=" + i);
        }
    }
    
    public static void main(String... a) {
        Thread t = new Thread(new ThreadTest());
        t.start();
    
        for (int i = 1; i <= 50; i++) {
            if (i == 10) {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main=" + i);
        }
    }
}

※i==10 後,會先將 join 的部分完成,最後才執行 main 的 Thread

※結果:
main=1
main=2
main=3
...
main=9
join=1
join=2
join=3
...
join=50
main=10
main=11
main=12
...
main=50



※yield

屈服的意思,是靜態方法,在哪個 Thread 寫 yield 就是哪個 Thread
使 Thread 讓出執行權,每個 Thread 會被分配到一小段 CPU 時間,時間結束就會換下一個 Thread,就算沒執行完也會換,如果執行這個方法,表示讓出執行權,讓其他 Thread 有機會執行,但也不是強制的


public class ThreadTest extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 50; i++) {
            if (i % 10 == 0) {
                Thread.yield();
            }
            System.out.println("yield=" + i);
        }
    }
    
    public static void main(String... a) {
        Thread t = new Thread(new ThreadTest());
        t.start();
    
        for (int i = 1; i <= 50; i++) {
            System.out.println("main=" + i);
        }
    }
}

※結果:
main=1
main=2
main=3
...
main=8
yield=1
yield=2
yield=3
...
yield=19
main=9
main=10
main=11
...
main=50
yield=20
yield=21
yield=22
...
yield=50

※以這個例子為例,一開始兩個 Thread 會互相取得執行權,10、20、30、40,yield 會讓 main 先執行,但因為不是強制的,所以10 並沒有換 main 執行,但到了20 就換 main 了,但 main 一口氣就執行完了,所以最後的30、40 也不用管 main 了



※sleep

sleep 和 yield 一樣,都是讓給其他 Thread 執行,也都是靜態方法


public class ThreadTest extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 50; i++) {
            if (i % 10 == 0) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("sleep=" + i);
        }
    }
    
    public static void main(String... a) {
        Thread t = new Thread(new ThreadTest());
        t.start();
    
        for (int i = 1; i <= 50; i++) {
            System.out.println("main=" + i);
        }
    }
}

※結果:
main=1
main=2
main=3
sleep=1
main=4
main=5
main=6
...
main=18
sleep=2
sleep=3
sleep=4
...
sleep=9
main=19
main=20
main=21
...
main=45
sleep=10
main=46
main=47
main=48
main=49
main=50
sleep=11
sleep=12
sleep=13
...
sleep=50



※interrupt

public class ThreadTest extends Thread {
    @Override
    public void run() {
        try {
            System.out.println("1");
            Thread.sleep(5000);
            System.out.println("2");
        } catch (InterruptedException e) {
            System.out.println("3");
            // return;
        }
        System.out.println("4");
    }
    
    public static void main(String... a) {
        Thread t = new Thread(new ThreadTest());
        t.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("5");
        t.interrupt();
        System.out.println("6");
    }
}

※結果:
1
5
3
4
6

※interrupt 後會被catch 攔截,但要注意如果沒有 return 或 break,還是會繼續執行



※yield 和 sleep 的差別

1.yield 不用參數,sleep 必需給參數
2.yield 只能讓同優先級的執行,但只是建議;sleep 不管優先級,只要是 Thread 就可執行
以上兩種方法不會釋放鎖
還有一個 Object 裡的 wait(),很像sleep,但會釋放鎖