JAVA多线程学习(一)
一、什么是多線程?
很顯然,一隨便編敲一段簡單的小代碼,從main方法開始運行,計算機是一條一條地從上而下的串行執行程序。那如果我們要同時執行兩個任務呢?就比如你用瀏覽器上網看網頁的時候,你還能同時登QQ,還能同時下載電影……這些任務都是并行執行的。在計算機的任務管理器里可以看到一個一個的正在運行的程序任務,這些程序就是一個個一個的進程,而大多數進程下面都已多條線程同時執行,所以,多線程其實就是多個任務在同時執行。
雖然是多線程,但是CPU執行命令仍然是一條一條執行的。然而計算機CPU只有一個,雖然多核計算機能夠有虛擬多CPU的技術(這是我自己對多核CPU的理解,不夠準確請見諒),但是相對于計算機要實現的線程數量,簡直少的可憐。所以,當多線程在硬件實現的時候,CPU的處理方式是進行分時處理(TDMA時分多址有沒有!!?)。什么意思呢,就是在一段時間執行這一條線程,一段時間執行另外一條線程,所有的具有執行資格的線程進行隨機的獲得CPU的執行權。CPU的運算速度快的驚人,因此我們感覺上其實就是多線程在并發執行了。
二、JAVA中多線程的使用,Thread類
“一切皆對象”在JAVA語言中是一個永恒不變的定理,自然線程這玩意同樣有個對象進行包裝。那就是Thread類。這個對象實現了一個Runnable接口,這個接口很簡單,就一個run方法。
對于要多線程執行任務,可以通過如下兩種方法來實現多線程:
1、繼承Thread類
通過將要實現的任務封裝成方法,再封裝成對象,然后繼承Thread類,將任務方法寫進run方法之中,在再父線程中用start方法啟動就可以了。比如如下開啟一個線程的代碼
package com.qyz.thread;public class ThreadDemo1 {public static void main(String[] args) {Thread t1 = new Demo("第一個線程");//start()開啟線程 t1.start();//輸出:第一個線程:被打開了 } } //繼承了Thread的對象 class Demo extends Thread{public Demo(String name){//父類構造方法中可以給線程命名super(name);}//重寫run方法 @Overridepublic void run(){//打印線程名稱System.out.println(Thread.currentThread().getName() + ":被打開了");} }?
?
2、通過實現Runnable接口
方法一的線程開啟方法有一個問題,就是當一個類的的繼承體系結構之中,只有部分子類需要開啟新的線程執行任務,但由于是單繼承的,繼承了某個父類的子類不可能再繼承Thread方法,因此只有通過實現
Runnable方法來開啟線程。
1、某個類實現Runnable,并重寫run方法,封裝需要新開啟線程實現的任務。2、新建Thread(Runnable target,name)對象。將Runnable對象封裝進線程對象中。3、開啟線程start()
1 package com.qyz.thread; 2 3 public class ThreadDemo2 { 4 5 public static void main(String[] args) { 6 Thread t1 = new Thread(new Demo2(),"第二個線程"); 7 //start()開啟線程 8 t1.start(); 9 //輸出:第二個線程:被打開了 10 } 11 } 12 //實現了Runnable接口的對象 13 class Demo2 implements Runnable{ 14 //重寫run方法 15 @Override 16 public void run(){ 17 //打印線程名稱 18 System.out.println(Thread.currentThread().getName() + ":被打開了"); 19 } 20 }?
三、多線程之間的同步問題
多線程好嗎?當然好,但是會有一個問題需要注意!那就是同步問題。在多個線程訪問同一對象數據時?有多條操作命令,本來想的是等一個線程操作完該對象后,另外一個線程再操作,但是由于CPU是隨機分時執行線程,因此有可能當一個線程對共享對象操作到一半時,CPU就切向另外一個線程,導致出現意外的輸出結果。這樣的問題一半很難排查,因此需要在編寫代碼的時候多加注意!
沒有同步的買賣票事例
1 package com.qyz.thread; 2 3 public class TickerDemo1 { 4 public static void main(String[] args) { 5 SaleTicket st = new SaleTicket(); 6 //實現3個線程 7 Thread t1 = new Thread(st, "1號賣票員"); 8 Thread t2 = new Thread(st, "2號賣票員"); 9 Thread t3 = new Thread(st, "3號賣票員"); 10 //開啟線程 11 t1.start(); 12 t2.start(); 13 t3.start(); 14 } 15 } 16 class SaleTicket implements Runnable{ 17 //總共100張票 18 private int num = 100; 19 @Override 20 public void run() { 21 while(this.num > 0){ 22 //這個代碼太簡單,所以讓線程睡一會,突出同步問題 23 try {Thread.sleep(10);} catch (Exception e) {} 24 System.out.println(Thread.currentThread().getName() + "******" + num--); 25 } 26 } 27 }?
本來不該出現的0號票出現了,意外情況
解決方法:加鎖
多線程中的鎖其實就像一扇門,一個線程進去之后,把門鎖上,另外一個線程就不能進來,直到門里面的線程執行完畢出來。
JAVA中實現同步鎖通過關鍵字synchronized,有以下方法
1、同步代碼塊,需要同步的命令封裝進一個代碼塊中,加上一個對象做鎖即可
1 class SaleTicket implements Runnable{ 2 //總共100張票 3 private int num = 100; 4 //一個object對象充當鎖 5 private Object lock = new Object(); 6 @Override 7 public void run() { 8 while(true){ 9 //同步代碼塊封裝 10 synchronized (lock) { 11 if(this.num > 0){ 12 //這個代碼太簡單,所以讓線程睡一會,突出同步問題 13 try {Thread.sleep(10);} catch (Exception e) {} 14 System.out.println(Thread.currentThread().getName() + "******" + num--); 15 }else 16 break; 17 } 18 } 19 } 20 }?
?
?輸出如下
問題解決!
2、同步函數
函數是一種封裝,代碼塊也是一種封裝,那能不能結合在一起呢?sure!那就是通過關鍵字將將方法變成同步函數。
1 class SaleTicket implements Runnable{ 2 //總共100張票 3 private int num = 100; 4 @Override 5 public void run() { 6 while(true){ 7 this.sale(); 8 } 9 } 10 //通過封裝成同步函數來實現 11 private synchronized void sale(){ 12 if(this.num > 0){ 13 //這個代碼太簡單,所以讓線程睡一會,突出同步問題 14 try {Thread.sleep(10);} catch (Exception e) {} 15 System.out.println(Thread.currentThread().getName() + "******" + num--); 16 } 17 } 18 }?
成功解決!
轉載于:https://www.cnblogs.com/njupt-Qsimple/p/5618491.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的JAVA多线程学习(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于MySQL的各种总结
- 下一篇: radio选中