java線程之基礎學習總結(二)
首先解釋第一節中提到“不要調用Thread類或Runnable對象的run方法。直接調用run方法,只會執行同一個線程中的任務,而不會啓動新線程。應該調用Thread.start()方法。這個方法將創建一個執行run方法的新線程。”因爲run方法驅動的是main線程,不會啓動其他新線程。但是如果使用start()方法就可以啓動新線程,且默認優先級與main線程一樣。都是Thread.NORM_PRIORITY=5。就可出現搶佔資源的現象了!!!!!
實例:
- public class RunStart implements Runnable {
- public void run() {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName() + "線程正運行 i="+ i);
- }
- }
- }
編寫TestRunStart類調用:
- public class TestRunStart {
- public static void main(String[] args) {
- RunStart runStart = new RunStart();
- Thread thread1 = new Thread(runStart, "thread2");
- Thread thread2 = new Thread(runStart, "thread2");
- thread1.run();
- thread2.run();
- }
- }
運行結果:
- main線程正運行 i=0
- main線程正運行 i=1
- main線程正運行 i=2
- main線程正運行 i=3
- main線程正運行 i=4
- main線程正運行 i=0
- main線程正運行 i=1
- main線程正運行 i=2
- main線程正運行 i=3
- main線程正運行 i=4
把TestRunStart類改一下如下:
- public class TestRunStart {
- public static void main(String[] args) {
- RunStart runStart = new RunStart();
- Thread thread1 = new Thread(runStart);
- Thread thread2 = new Thread(runStart);
- thread1.start();
- thread2.start();
- }
- }
運行結果:
- Thread-0線程正運行 i=0
- Thread-0線程正運行 i=1
- Thread-0線程正運行 i=2
- Thread-0線程正運行 i=3
- Thread-0線程正運行 i=4
- Thread-1線程正運行 i=0
- Thread-1線程正運行 i=1
- Thread-1線程正運行 i=2
- Thread-1線程正運行 i=3
- Thread-1線程正運行 i=4
就是系統默認的名字了!!!
線程優先級:
- public class PriorityTest {
- public static void main(String[] args) {
- System.out.println("Thread.MAX_PRIORITY : " + Thread.MAX_PRIORITY);
- System.out.println("Thread.NORM_PRIORITY: " + Thread.NORM_PRIORITY);
- System.out.println("Thread.MIN_PRIORITY : " + Thread.MIN_PRIORITY);
- }
- }
運行結果:
- Thread.MAX_PRIORITY : 10
- Thread.NORM_PRIORITY: 5
- Thread.MIN_PRIORITY : 1
看看main線程的優先級:
- public class RunStart implements Runnable {
- public void run() {
- switch (Thread.currentThread().getPriority()) {
- case 1:
- System.out.println(Thread.currentThread().getName()
- + "線程優先級:Thread.MIN_PRIORITY="
- + Thread.currentThread().getPriority());
- break;
- case 5:
- System.out.println(Thread.currentThread().getName()
- + "線程優先級:Thread.NORM_PRIORITY="
- + Thread.currentThread().getPriority());
- break;
- case 10:
- System.out.println(Thread.currentThread().getName()
- + "線程優先級:Thread.MAX_PRIORITY="
- + Thread.currentThread().getPriority());
- break;
- default:
- System.out
- .println(Thread.currentThread().getName() + "線程優先級:ERORR");
- break;
- }
- }
- }
調用:
- public class TestRunStart {
- public static void main(String[] args) {
- RunStart runStart = new RunStart();
- Thread thread1 = new Thread(runStart, "thread1");
- thread1.run();
- }
- }
運行結果:
- main線程優先級:Thread.NORM_PRIORITY=5
看看創建新的線程的默認優先級:
- public class TestRunStart {
- public static void main(String[] args) {
- RunStart runStart = new RunStart();
- Thread thread1 = new Thread(runStart, "singsong");
- thread1.start();
- }
- }
運行結果:
- singsong線程優先級:Thread.NORM_PRIORITY=5
在優先級相同的情況下,就出現了CPU資源的搶佔現象了
從以上的運行結果可以知道main方法其實也是一個線程。在java中所有的線程都是同時啓動的,至於什麼時候,哪個先執行,完全看誰先得到CPU的資源。
在java中,每次程序運行至少啓動2個線程。一個是main線程,一個是垃圾收集線程。因爲每當使用java命令執行一個類的時候,實際上都會啓動一個JVM,每一個jVM實習在就是在操作系統中啓動了一個進程。
線程的中斷
實例:
- public class SleepInterrupt implements Runnable {
- public void run() {
- System.out.println("1、進入run方法體線程正常啓動!!");
- try {
- System.out.println("2、線程休眠3秒");
- Thread.sleep(3000);
- System.out.println("3、線程正常休眠3秒");
- } catch (InterruptedException e) {
- System.out.println("4、線程被中斷了!!!");
- }
- System.out.println("5、正常結束run方法");
- System.out.println(Thread.currentThread().getName() + "線程正在運行");
- }
- }
TestSleepInterrupt類調用:
- public class TestSleepInterrupt {
- public static void main(String[] args) {
- SleepInterrupt sleepInterrupt = new SleepInterrupt();
- Thread thread = new Thread(sleepInterrupt, "singsong");
- thread.start();
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- thread.interrupt();
- }
- }
運行結果:
- 1、進入run方法體線程正常啓動!!
- 2、線程休眠3秒
- 3、線程正常休眠3秒
- 5、正常結束run方法
- singsong線程正在運行
在啓動線程時,要若不加此句“Thread.sleep(3000);”程序進入執行中斷操作,
執行結果:
- 1、進入run方法體線程正常啓動!!
- 2、線程休眠3秒
- 4、線程被中斷了!!!
- 5、正常結束run方法
- singsong線程正在運行
執行線程睡眠操作,拋出中斷異常,因此要有異常處理
public static void sleep(long millis, int nanos) throws InterruptedException
線程的強制執行jion()方法:
- public class JoinDemo implements Runnable {
- public void run() {
- for (int i = 0; i < 10; i++) {
- System.out.println(Thread.currentThread().getName() + i);
- }
- }
- public static void main(String[] args) {
- Thread thread = new Thread(new JoinDemo());
- thread.setName("singsong");
- thread.start();
- for (int i = 0; i < 10; i++) {
- if (i == 5) {
- try {
- thread.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- System.out.println(Thread.currentThread().getName() + i);
- }
- }
- }
運行結果:
- main0
- main1
- main2
- main3
- main4
- singsong0
- singsong1
- singsong2
- singsong3
- singsong4
- singsong5
- singsong6
- singsong7
- singsong8
- singsong9
- main5
- main6
- main7
- main8
- main9
從運行結果中:本程序啓動了兩個線程:一個是main線程,另一個是singsong線程
當main線程中循環執行到第五次時,就強制執行singsong線程。Join()方法就是用來強制某一線程的運行。
後臺線程與setDaemon()方法
對java 程序來說,只要還有一個前臺線程在運行,這個進程就不還結束,如果一個進程中只有後臺線程在運行,這個進程就會結束。前臺進程是相對後臺線程而言的。如果某個線程對象在啓動(調用start()方法)之前調用了setDaemon(true)方法,這個線程就變成了後臺線程。
實例:
- public class SetNameDemo implements Runnable {
- public void run() {
- while (true) {
- System.out
- .println(Thread.currentThread().getName() + " is running");
- }
- }
- public static void main(String[] args) {
- Thread thread = new Thread(new SetNameDemo());
- thread.setName("singsong");
- thread.setDaemon(true);
- thread.start();
- }
- }
運行結果:
- singsong is running
- singsong is running
- singsong is running
- singsong is running
- singsong is running
從運行結果可以看到:雖然創建了一個無限循環的線程,因爲它是後臺線程,因此整個進程在主線程結束時就隨之終止運行了。
同步與死鎖
多個線程共享同一個資源時需要進行同步,但是過多的同步會造成死鎖。
實例:
- public class Synchronized implements Runnable {
- private int ticket = 5;
- public void run() {
- for (int i = 0; i < 10; i++) {
- if (ticket > 0) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "在售出第"
- + ticket-- + "票");
- }
- }
- }
- }
SynchronizedTest類調用:
- public class SynchronizedTest {
- public static void main(String[] args) {
- Synchronized syn=new Synchronized();
- Thread thread =new Thread(syn ,"singsong");
- Thread thread1=new Thread(syn,"singsong1");
- thread.start();
- thread1.start();
- }
- }
運行結果:
- singsong1在售出第5票
- singsong在售出第4票
- singsong1在售出第3票
- singsong在售出第2票
- singsong1在售出第1票
- singsong在售出第0票
出現這個問題,是在程序中判斷剩餘票數與修改票數之間加入了睡眠語句(類似於網絡不好,有延遲時)。
如果想要解決這樣的問題,就必須使用同步,所謂的同步就是指多個操作在同一個時間段內只能有一個線程進行,其他線程要等待此線程完成之後纔可以繼續執行。
在java中可以通過同步代碼的方式進行代碼的加鎖操作,通過加鎖可以保證同一個時間段內只能有一個線程進行。同步的實現有兩種方式。
同步代碼塊
語法格式:
synchronized(同步對象){//但是一般都把當前對象this作爲同步對象。
//需要同步的代碼
}
同步方法:
語法格式:
synchronized 方法返回類型方法名(參數列表){
// 其他代碼
}
實例:
同步代碼塊
- public void run() {
- for (int i = 0; i < 10; i++) {
- synchronized (this) {//同步代碼塊
- if (ticket > 0) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName()
- + "在售出第" + ticket-- + "票");
- }
- }
- }
- }
同步方法
- public class Synchronized implements Runnable {
- private int ticket = 5;
- public void run() {
- for (int i = 0; i < 10; i++) {
- sale();
- }
- }
- //同步方法sale()
- public synchronized void sale() {
- if (ticket > 0) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "在售出第"
- + ticket-- + "票");
- }
- }
- }
運行結果:
- singsong在售出第5票
- singsong在售出第4票
- singsong在售出第3票
- singsong在售出第2票
- singsong1在售出第1票