多線程1
線程:線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是可以有多個線程的,這個應用程序也可以稱之為多線程程序。
進程:進程指正在運行的程序。確切的來說,當一個程序進入內存運行,即變成一個進程,進程是處於運行過程中的程序,並且具有一定獨立功能
。
什麽是多線程呢?即就是一個程序中有多個線程在同時執行。
通過下圖來區別單線程程序與多線程程序的不同:
? 單線程程序:即,若有多個任務只能依次執行。當上一個任務執行結束後,下一個任務開始執行。如,去網吧上網,網吧只能讓一個人上網,當這個人下機後,下一個人才能上網。
? 多線程程序:即,若有多個任務可以同時執行。如,去網吧上網,網吧能夠讓多個人同時上網。
? 分時調度
所有線程輪流使用 CPU 的使用權,平均分配每個線程占用 CPU 的時間。
? 搶占式調度
優先讓優先級高的線程使用 CPU,如果線程的優先級相同,那麽會隨機選擇一個(線程隨機性),Java使用的為搶占式調度。
大部分操作系統都支持多進程並發運行,現在的操作系統幾乎都支持同時運行多個程序。比如:現在我們上課一邊使用編輯器,一邊使用錄屏軟件,同時還開著畫圖板,dos窗口等軟件。此時,這些程序是在同時運行,”感覺這些軟件好像在同一時刻運行著“。
實際上,CPU(中央處理器)使用搶占式調度模式在多個線程間進行著高速的切換。對於CPU的一個核而言,某個時刻,只能執行一個線程,而 CPU的在多個線程間切換速度相對我們的感覺要快,看上去就是在同一時刻運行。
其實,多線程程序並不能提高程序的運行速度,但能夠提高程序運行效率,讓CPU的使用率更高。
原因是:jvm啟動後,必然有一個執行路徑(線程)從main方法開始的,一直執行到main方法結束,這個線程在java中稱之為主線程。當程序的主線程執行時,如果遇到了循環而導致程序在指定位置停留時間過長,則無法馬上執行下面的程序,需要等待循環結束後能夠執行。
那麽,能否實現一個主線程負責執行其中一個循環,再由另一個線程負責其他代碼的執行,最終實現多部分代碼同時執行的效果?
能夠實現同時執行,通過Java中的多線程技術來解決該問題。
? 一種方法是將類聲明為 Thread 的子類。該子類應重寫 Thread 類的 run 方法。創建對象,開啟線程。run方法相當於其他線程的main方法。
? 另一種方法是聲明一個實現 Runnable 接口的類。該類然後實現 run 方法。然後創建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。
SubThread類
package cn.itcast.thread;
public class SubThread extends Thread {
//繼承Thread,重寫方法run()
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
System.out.println("run" + i);
}
}
}
demo測試類
package cn.itcast.thread;
//如何創建和啟動線程
public class Demo {
public static void main(String[] args) {
SubThread st = new SubThread();
st.start();//start()方法是父類的,必須調start(),jvm開啟新線程才調用了run,直接手動手動調用run()是不對的,這樣的話還是個單線程程序
for (int i = 0; i < 50; i++) {
System.out.println("main" + i);
}
}
}
結果:(每次運行都會變化)
main0
main1
main2
main3
run0
main4
main5
main6
main7
main8
main9
main10
main11
main12
main13
main14
run1
run2
run3
run4
run5
run6
run7
run8
run9
run10
run11
run12
run13
run14
run15
run16
run17
run18
run19
run20
run21
run22
run23
run24
run25
run26
run27
run28
run29
run30
run31
run32
run33
run34
run35
run36
run37
run38
run39
run40
run41
run42
run43
run44
run45
run46
run47
run48
run49
main15
main16
main17
main18
main19
main20
main21
main22
main23
main24
main25
main26
main27
main28
main29
main30
main31
main32
main33
main34
main35
main36
main37
main38
main39
main40
main41
main42
main43
main44
main45
main46
main47
main48
main49
思考:線程對象調用 run方法和調用start方法區別?
線程對象調用run方法不開啟線程。僅是對象調用方法。線程對象調用start開啟線程,並讓jvm調用run方法在開啟的線程中執行。
我們為什麽要繼承Thread類,並調用其的start方法才能開啟線程呢?
繼承Thread類:因為Thread類用來描述線程,具備線程應該有功能。那為什麽不直接創建Thread類的對象呢?如下代碼:
Thread t1 = new Thread();
t1.start();//這樣做沒有錯,但是該start調用的是Thread類中的run方法,而這個run方法沒有做什麽事情,更重要的是這個run方法中並沒有定義我們需要讓線程執行的代碼。
創建線程的目的是什麽?
是為了建立程序單獨的執行路徑,讓多部分代碼實現同時執行。也就是說線程創建並執行需要給定線程要執行的任務。
對於之前所講的主線程,它的任務定義在main函數中。自定義線程需要執行的任務都定義在run方法中。
Thread類run方法中的任務並不是我們所需要的,只有重寫這個run方法。既然Thread類已經定義了線程任務的編寫位置(run方法),那麽只要在編寫位置(run方法)中定義任務代碼即可。所以進行了重寫run方法動作。
? Thread.currentThread()獲取當前線程對象
? Thread.currentThread().getName();獲取當前線程對象的名稱
通過結果觀察,原來主線程的名稱:main;自定義的線程:Thread-0,線程多個時,數字順延。如Thread-1......
異常看名字:
NameThread類
package cn.itcast.threadname;
public class NameThread extends Thread {
public void run() {
System.out.println(0/0);
}
}
ThreadDemo類
package cn.itcast.threadname;
public class ThreadDemo {
public static void main(String[] args) {
NameThread nt = new NameThread();
//nt.run();
/*
*
* Exception in thread "main" java.lang.ArithmeticException: / by zero
at cn.itcast.threadname.NameThread.run(NameThread.java:5)
at cn.itcast.threadname.ThreadDemo.main(ThreadDemo.java:7)
*/
nt.start();
/*
*
* Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at cn.itcast.threadname.NameThread.run(NameThread.java:5)
*/
}
}
看線程名字:getName();
package cn.itcast.threadname;
public class NameThread extends Thread {
public void run() {
//System.out.println(0/0);
System.out.println(getName());
}
}
通用得到線程名字
System.out.println(Thread.currentThread().getName());
改線程名,先改名,後開啟線程
nt.setName("hah");
子類構造器改名
public NameThread(String name) {
super(name);
// TODO Auto-generated constructor stub
}
NameThread nt = new NameThread("hah");
sleep是靜態方法
package cn.itcast.sleep;
public class Demo {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 5; i++) {
Thread.sleep(50);//sleep是靜態方法
System.out.println(i);
}
}
}
多線程實現接口方式
RunnableDemo類
package cn.itcast.runnable;
public class RunnableDemo {
public static void main(String[] args) {
SubRunnable sr = new SubRunnable();
Thread t = new Thread(sr);
t.start();
for (int i = 0; i < 50; i++) {
System.out.println("main--" + i);
}
}
}
SubRunnable類
package cn.itcast.runnable;
public class SubRunnable implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
System.out.println("run--" + i);
}
}
}
創建線程的另一種方法是聲明實現 Runnable 接口的類。該類然後實現 run 方法。然後創建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。
實現Runnable接口,避免了繼承Thread類的單繼承局限性。覆蓋Runnable接口中的run方法,將線程任務代碼定義到run方法中。
創建Thread類的對象,只有創建Thread類的對象才可以創建線程。線程任務已被封裝到Runnable接口的run方法中,而這個run方法所屬於Runnable接口的子類對象,所以將這個子類對象作為參數傳遞給Thread的構造函數,這樣,線程對象創建時就可以明確要運行的線程的任務。
1.6.2 實現Runnable的好處
第二種方式實現Runnable接口避免了單繼承的局限性,所以較為常用。實現Runnable接口的方式,更加的符合面向對象,線程分為兩部分,一部分線程對象,一部分線程任務。繼承Thread類,線程對象和線程任務耦合在一起。一旦創建Thread類的子類對象,既是線程對象,有又有線程任務。實現runnable接口,將線程任務單獨分離出來封裝成對象,類型就是Runnable接口類型。Runnable接口對線程對象和線程任務進行解耦。
匿名內部類實現多線程
package cn.itcast.anonymous;
public class Demo {
public static void main(String[] args) {
new Thread(){
public void run() {
System.out.println("---");
}
}.start();
new Thread(new Runnable(){
public void run(){
System.out.println("0000000");
}
}).start();
}
}
實現線程池
package cn.itcast.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//使用jdk1.5新特性,實現線程池,使用工廠類Executor中的靜態方法創建線程對象,指定線程的個數
public class ThreadPoolDemo {
public static void main(String[] args) {
//調用工廠類創建線程池對象
ExecutorService threadPool = Executors.newFixedThreadPool(2);
threadPool.submit(new ThreadPoolRunnable());
threadPool.submit(new ThreadPoolRunnable());
threadPool.submit(new ThreadPoolRunnable());
threadPool.shutdown();
}
}
package cn.itcast.threadpool;
public class ThreadPoolRunnable implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("線程提交的任務" + Thread.currentThread().getName());
}
}
實現Callable接口的多線程,可以有返回值,可以拋異常
package cn.itcast.callable;
import java.util.concurrent.Callable;
public class CallableDemo implements Callable<String>{
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "abc";
}
}
package cn.itcast.callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Demo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService es = Executors.newFixedThreadPool(2);
Future<String> submit = es.submit(new CallableDemo());
System.out.println(submit.get());
}
}
案例:用多線程計算求和
package cn.itcast.casedemo;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CaseDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> f1 = pool.submit(new SumCallable(100));
Future<Integer> f2 = pool.submit(new SumCallable(200));
System.out.println(f1.get() + "--------" + f2.get());
pool.shutdown();
}
}
package cn.itcast.casedemo;
import java.util.concurrent.Callable;
public class SumCallable implements Callable<Integer>{
private int a;
public SumCallable(int a) {
this.a = a;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= a; i++) {
sum += i;
}
return sum;
}
}
註意call是沒有參數的,傳參只能通過構造函數.
多線程1