java多線程代碼
1. 什麼是java多線程詳解
線程對象是可以產生線程的對象。比如在Java平台中Thread對象,Runnable對象。線程,是指正在執行的一個指點令序列。在java平台上是指從一個線程對象的start()開始,運行run方法體中的那一段相對獨立的過程。相比於多進程,多線程的優勢有:
(1)進程之間不能共享數據,線程可以;
(2)系統創建進程需要為該進程重新分配系統資源,故創建線程代價比較小;
(3)Java語言內置了多線程功能支持,簡化了java多線程編程。
一、創建線程和啟動
(1)繼承Thread類創建線程類
通過繼承Thread類創建線程類的具體步驟和具體代碼如下:
• 定義一個繼承Thread類的子類,並重寫該類的run()方法;
• 創建Thread子類的實例,即創建了線程對象;
• 調用該線程對象的start()方法啟動線程。
復制代碼
class SomeThead extends Thraad {
public void run() {
//do something here
}
}
public static void main(String[] args){
SomeThread oneThread = new SomeThread();
步驟3:啟動線程:
oneThread.start();
}
復制代碼
(2)實現Runnable介面創建線程類
通過實現Runnable介面創建線程類的具體步驟和具體代碼如下:
• 定義Runnable介面的實現類,並重寫該介面的run()方法;
• 創建Runnable實現類的實例,並以此實例作為Thread的target對象,即該Thread對象才是真正的線程對象。
復制代碼
class SomeRunnable implements Runnable {
public void run() {
//do something here
}
}
Runnable oneRunnable = new SomeRunnable();
Thread oneThread = new Thread(oneRunnable);
oneThread.start();
復制代碼
(3)通過Callable和Future創建線程
通過Callable和Future創建線程的具體步驟和具體代碼如下:
• 創建Callable介面的實現類,並實現call()方法,該call()方法將作為線程執行體,並且有返回值。
• 創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。
• 使用FutureTask對象作為Thread對象的target創建並啟動新線程。
• 調用FutureTask對象的get()方法來獲得子線程執行結束後的返回值其中,Callable介面(也只有一個方法)定義如下:
復制代碼
public interface Callable {
V call() throws Exception;
}
步驟1:創建實現Callable介面的類SomeCallable(略);
步驟2:創建一個類對象:
Callable oneCallable = new SomeCallable();
步驟3:由Callable創建一個FutureTask對象:
FutureTask oneTask = new FutureTask(oneCallable);
注釋: FutureTask是一個包裝器,它通過接受Callable來創建,它同時實現了 Future和Runnable介面。
步驟4:由FutureTask創建一個Thread對象:
Thread oneThread = new Thread(oneTask);
步驟5:啟動線程:
oneThread.start();
2. java多線程如何創建多個多線程
Java 多線程的同步依靠的是對象鎖機制,這個問題需要我們不斷的學習相關的問題。下面我們就來詳細的學習下如何才能更好的進行具體內容的使用。synchronized關鍵字的背後就是利用了封鎖來實現對共享資源的互斥訪問。
下面以一個簡單的實例來進行對比分析。實例要完成的工作非常簡單,就是創建10個線程,每個線程都列印從0到99這100個數字,我們希望線程之間不會出現交叉亂序列印,而是順序地列印。
先來看第一段代碼,這里我們在run()方法中加入了synchronized關鍵字,希望能對run方法進行互斥訪問,但結果並不如我們希望那樣,這是因為這里synchronized鎖住的是this對象,即當前運行線程對象本身。 Java 多線程代碼中創建了10個線程,而每個線程都持有this對象的對象鎖,這不能實現線程的同步。
Java多線程代碼如下
1.package com.vista;
2.class MyThread implements java.lang.Runnable
3.{
4.private int threadId;
5.public MyThread(int id)
6.{
7.this.threadId = id;
8.}
9.@Override
10.public synchronized void run()
11.{
12.for (int i = 0; i < 100; ++i)
13.{
14.System.out.println("Thread ID: " + this.threadId + " : " + i);
15.}
16.}
17.}
18.public class ThreadDemo
19.{
20./**
21.* @param args
22.* @throws InterruptedException
23.*/
24.public static void main(String[] args) throws InterruptedException
25.{
26.for (int i = 0; i < 10; ++i)
27.{
28.new Thread(new MyThread(i)).start();
29.Thread.sleep(1);
30.}
31.}
32.}
以上就是對Java多線程的詳細代碼介紹。
3. Java語言:Java多線程怎樣創建
Java提供了線程類Thread來創建多線程的程序。其實,創建線程與創建普通的類的對象的操作是一樣的,而線程就是Thread類或其子類的實例對象。每個Thread對象描述了一個單獨的線程。要產生一個線程,有兩種方法:
需要從Java.lang.Thread類派生一個新的線程類,重載它的run()方法;
實現Runnalbe介面,重載Runnalbe介面中的run()方法。
但,為什麼Java要提供兩種方法來創建線程呢?它們都有哪些區別?相比而言,哪一種方法更好呢?
在Java中,類僅支持單繼承,也就是說,當定義一個新的類的時候,它只能擴展一個外部類.這樣,如果創建自定義線程類的時候是通過擴展 Thread類的方法來實現的,那麼這個自定義類就不能再去擴展其他的類,也就無法實現更加復雜的功能。因此,如果自定義類必
須擴展其他的類,那麼就可以使用實現Runnable介面的方法來定義該類為線程類,這樣就可以避免Java單繼承所帶來的局限性。
還有一點最重要的就是使用實現Runnable介面的方式創建的線程可以處理同一資源,從而實現資源的共享.
(1)通過擴展Thread類來創建多線程
假設一個影院有三個售票口,分別用於向兒童、成人和老人售票。影院為每個窗口放有100張電影票,分別是兒童票、成人票和老人票。三個窗口需要同時賣票,而現在只有一個售票員,這個售票員就相當於一個CPU,三個窗口就相當於三個線程。通過程序來看一看是如
何創建這三個線程的。
public class MutliThreadDemo {
public static void main(String [] args){
MutliThread m1=new MutliThread("Window 1");
MutliThread m2=new MutliThread("Window 2");
MutliThread m3=new MutliThread("Window 3");
m1.start();
m2.start();
m3.start();
}
}
class MutliThread extends Thread{
private int ticket=100;//每個線程都擁有100張票
MutliThread(String name){
super(name);//調用父類帶參數的構造方法
}
public void run(){
while(ticket>0){
System.out.println(ticket--+" is saled by "+Thread.currentThread().getName());
}
}
}
程序中定義一個線程類,它擴展了Thread類。利用擴展的線程類在MutliThreadDemo類的主方法中創建了三個線程對象,並通過start()方法分別將它們啟動。
從結果可以看到,每個線程分別對應100張電影票,之間並無任何關系,這就說明每個線程之間是平等的,沒有優先順序關系,因此都有機會得到CPU的處理。但是結果顯示這三個線程並不是依次交替執行,而是在三個線程同時被執行的情況下,有的線程被分配時間片的機
會多,票被提前賣完,而有的線程被分配時間片的機會比較少,票遲一些賣完。
可見,利用擴展Thread類創建的多個線程,雖然執行的是相同的代碼,但彼此相互獨立,且各自擁有自己的資源,互不幹擾。
(2)通過實現Runnable介面來創建多線程
public class MutliThreadDemo2 {
public static void main(String [] args){
MutliThread m1=new MutliThread("Window 1");
MutliThread m2=new MutliThread("Window 2");
MutliThread m3=new MutliThread("Window 3");
Thread t1=new Thread(m1);
Thread t2=new Thread(m2);
Thread t3=new Thread(m3);
t1.start();
t2.start();
t3.start();
}
}
class MutliThread implements Runnable{
private int ticket=100;//每個線程都擁有100張票
private String name;
MutliThread(String name){
this.name=name;
}
public void run(){
while(ticket>0){
System.out.println(ticket--+" is saled by "+name);
}
}
}
由於這三個線程也是彼此獨立,各自擁有自己的資源,即100張電影票,因此程序輸出的結果和(1)結果大同小異。均是各自線程對自己的100張票進行單獨的處理,互不影響。
可見,只要現實的情況要求保證新建線程彼此相互獨立,各自擁有資源,且互不幹擾,採用哪個方式來創建多線程都是可以的。因為這兩種方式創建的多線程程序能夠實現相同的功能。
由於這三個線程也是彼此獨立,各自擁有自己的資源,即100張電影票,因此程序輸出的結果和例4.2.1的結果大同小異。均是各自線程對自己的100張票進行單獨的處理,互不影響。
可見,只要現實的情況要求保證新建線程彼此相互獨立,各自擁有資源,且互不幹擾,採用哪個方式來創建多線程都是可以的。因為這兩種方式創建的多線程程序能夠實現相同的功能。
(3)通過實現Runnable介面來實現線程間的資源共享
現實中也存在這樣的情況,比如模擬一個火車站的售票系統,假如當日從A地發往B地的火車票只有100張,且允許所有窗口賣這100張票,那麼每一個窗口也相當於一個線程,但是這時和前面的例子不同之處就在於所有線程處理的資源是同一個資源,即100張車票。如果
還用前面的方式來創建線程顯然是無法實現的,這種情況該怎樣處理呢?看下面這個程序,程序代碼如下所示:
public class MutliThreadDemo3 {
public static void main(String [] args){
MutliThread m=new MutliThread();
Thread t1=new Thread(m,"Window 1");
Thread t2=new Thread(m,"Window 2");
Thread t3=new Thread(m,"Window 3");
t1.start();
t2.start();
t3.start();
}
}
class MutliThread implements Runnable{
private int ticket=100;//每個線程都擁有100張票
public void run(){
while(ticket>0){
System.out.println(ticket--+" is saled by "+Thread.currentThread().getName());
}
}
}
結果正如前面分析的那樣,程序在內存中僅創建了一個資源,而新建的三個線程都是基於訪問這同一資源的,並且由於每個線程上所運行的是相同的代碼,因此它們執行的功能也是相同的。
可見,如果現實問題中要求必須創建多個線程來執行同一任務,而且這多個線程之間還將共享同一個資源,那麼就可以使用實現Runnable介面的方式來創建多線程程序。而這一功能通過擴展Thread類是無法實現的,讀者想想看,為什麼?
實現Runnable介面相對於擴展Thread類來說,具有無可比擬的優勢。這種方式不僅有利於程序的健壯性,使代碼能夠被多個線程共享,而且代碼和數據資源相對獨立,從而特別適合多個具有相同代碼的線程去處理同一資源的情況。這樣一來,線程、代碼和數據資源三者
有效分離,很好地體現了面向對象程序設計的思想。因此,幾乎所有的多線程程序都是通過實現Runnable介面的方式來完成的。
4. java多線程共同操作同一個隊列,怎麼實現
具體代碼如下:
5. Java澶氱嚎紼嬪垵瀛﹁呮寚鍗楋紙3錛夛細浣跨敤Runnable鎺ュ彛鍒涘緩綰跨▼
銆銆涓婁竴綃 Java澶氱嚎紼嬪垵瀛﹁呮寚鍗楋紙 錛 鐢═hread綾誨壋寤虹嚎紼
銆銆瀹炵幇Runnable鎺ュ彛鐨勭被蹇呴』浣跨敤Thread綾葷殑瀹炰緥鎵嶈兘鍒涘緩綰跨▼ 閫氳繃Runnable鎺ュ彛鍒涘緩綰跨▼鍒嗕負涓ゆ
銆銆 灝嗗疄鐜癛unnable鎺ュ彛鐨勭被瀹炰緥鍖
銆銆 寤虹珛涓涓猅hread瀵硅薄 騫跺皢絎涓姝ュ疄渚嬪寲鍚庣殑瀵硅薄浣滀負鍙傛暟浼犲叆Thread綾葷殑鏋勯犳柟娉
銆銆鏈鍚庨氳繃Thread綾葷殑start鏂規硶寤虹珛綰跨▼
銆銆涓嬮潰鐨勪唬鐮佹紨紺轟簡濡備綍浣跨敤Runnable鎺ュ彛鏉ュ壋寤虹嚎紼
銆銆 packagemythread;{publicvoidrun(){System out println(Thread currentThread() getName());}publicstaticvoidmain(String[]args){MyRunnablet =newMyRunnable();MyRunnablet =newMyRunnable();Threadthread =newThread(t MyThread );Threadthread =newThread(t );thread setName( MyThread );thread start();thread start();}}
銆銆涓婇潰浠g爜鐨勮繍琛岀粨鏋滃備笅
lishixin/Article/program/Java/gj/201311/27466
6. 在Java 中多線程的實現方法有哪些,如何使用
Java多線程的創建及啟動
Java中線程的創建常見有如三種基本形式
1.繼承Thread類,重寫該類的run()方法。
復制代碼
1 class MyThread extends Thread {
2
3 private int i = 0;
4
5 @Override
6 public void run() {
7 for (i = 0; i < 100; i++) {
8 System.out.println(Thread.currentThread().getName() + " " + i);
9 }
10 }
11 }
復制代碼
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Thread myThread1 = new MyThread(); // 創建一個新的線程 myThread1 此線程進入新建狀態
8 Thread myThread2 = new MyThread(); // 創建一個新的線程 myThread2 此線程進入新建狀態
9 myThread1.start(); // 調用start()方法使得線程進入就緒狀態
10 myThread2.start(); // 調用start()方法使得線程進入就緒狀態
11 }
12 }
13 }
14 }
復制代碼
如上所示,繼承Thread類,通過重寫run()方法定義了一個新的線程類MyThread,其中run()方法的方法體代表了線程需要完成的任務,稱之為線程執行體。當創建此線程類對象時一個新的線程得以創建,並進入到線程新建狀態。通過調用線程對象引用的start()方法,使得該線程進入到就緒狀態,此時此線程並不一定會馬上得以執行,這取決於CPU調度時機。
2.實現Runnable介面,並重寫該介面的run()方法,該run()方法同樣是線程執行體,創建Runnable實現類的實例,並以此實例作為Thread類的target來創建Thread對象,該Thread對象才是真正的線程對象。
復制代碼
1 class MyRunnable implements Runnable {
2 private int i = 0;
3
4 @Override
5 public void run() {
6 for (i = 0; i < 100; i++) {
7 System.out.println(Thread.currentThread().getName() + " " + i);
8 }
9 }
10 }
復制代碼
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Runnable myRunnable = new MyRunnable(); // 創建一個Runnable實現類的對象
8 Thread thread1 = new Thread(myRunnable); // 將myRunnable作為Thread target創建新的線程
9 Thread thread2 = new Thread(myRunnable);
10 thread1.start(); // 調用start()方法使得線程進入就緒狀態
11 thread2.start();
12 }
13 }
14 }
15 }
復制代碼
相信以上兩種創建新線程的方式大家都很熟悉了,那麼Thread和Runnable之間到底是什麼關系呢?我們首先來看一下下面這個例子。
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Runnable myRunnable = new MyRunnable();
8 Thread thread = new MyThread(myRunnable);
9 thread.start();
10 }
11 }
12 }
13 }
14
15 class MyRunnable implements Runnable {
16 private int i = 0;
17
18 @Override
19 public void run() {
20 System.out.println("in MyRunnable run");
21 for (i = 0; i < 100; i++) {
22 System.out.println(Thread.currentThread().getName() + " " + i);
23 }
24 }
25 }
26
27 class MyThread extends Thread {
28
29 private int i = 0;
30
31 public MyThread(Runnable runnable){
32 super(runnable);
33 }
34
35 @Override
36 public void run() {
37 System.out.println("in MyThread run");
38 for (i = 0; i < 100; i++) {
39 System.out.println(Thread.currentThread().getName() + " " + i);
40 }
41 }
42 }
復制代碼
同樣的,與實現Runnable介面創建線程方式相似,不同的地方在於
1 Thread thread = new MyThread(myRunnable);
那麼這種方式可以順利創建出一個新的線程么?答案是肯定的。至於此時的線程執行體到底是MyRunnable介面中的run()方法還是MyThread類中的run()方法呢?通過輸出我們知道線程執行體是MyThread類中的run()方法。其實原因很簡單,因為Thread類本身也是實現了Runnable介面,而run()方法最先是在Runnable介面中定義的方法。
1 public interface Runnable {
2
3 public abstract void run();
4
5 }
我們看一下Thread類中對Runnable介面中run()方法的實現:
復制代碼
@Override
public void run() {
if (target != null) {
target.run();
}
}
復制代碼
也就是說,當執行到Thread類中的run()方法時,會首先判斷target是否存在,存在則執行target中的run()方法,也就是實現了Runnable介面並重寫了run()方法的類中的run()方法。但是上述給到的列子中,由於多態的存在,根本就沒有執行到Thread類中的run()方法,而是直接先執行了運行時類型即MyThread類中的run()方法。
3.使用Callable和Future介面創建線程。具體是創建Callable介面的實現類,並實現clall()方法。並使用FutureTask類來包裝Callable實現類的對象,且以此FutureTask對象作為Thread對象的target來創建線程。
看著好像有點復雜,直接來看一個例子就清晰了。
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4
5 Callable<Integer> myCallable = new MyCallable(); // 創建MyCallable對象
6 FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask來包裝MyCallable對象
7
8 for (int i = 0; i < 100; i++) {
9 System.out.println(Thread.currentThread().getName() + " " + i);
10 if (i == 30) {
11 Thread thread = new Thread(ft); //FutureTask對象作為Thread對象的target創建新的線程
12 thread.start(); //線程進入到就緒狀態
13 }
14 }
15
16 System.out.println("主線程for循環執行完畢..");
17
18 try {
19 int sum = ft.get(); //取得新創建的新線程中的call()方法返回的結果
20 System.out.println("sum = " + sum);
21 } catch (InterruptedException e) {
22 e.printStackTrace();
23 } catch (ExecutionException e) {
24 e.printStackTrace();
25 }
26
27 }
28 }
29
30
31 class MyCallable implements Callable<Integer> {
32 private int i = 0;
33
34 // 與run()方法不同的是,call()方法具有返回值
35 @Override
36 public Integer call() {
37 int sum = 0;
38 for (; i < 100; i++) {
39 System.out.println(Thread.currentThread().getName() + " " + i);
40 sum += i;
41 }
42 return sum;
43 }
44
45 }
復制代碼
首先,我們發現,在實現Callable介面中,此時不再是run()方法了,而是call()方法,此call()方法作為線程執行體,同時還具有返回值!在創建新的線程時,是通過FutureTask來包裝MyCallable對象,同時作為了Thread對象的target。那麼看下FutureTask類的定義:
1 public class FutureTask<V> implements RunnableFuture<V> {
2
3 //....
4
5 }
1 public interface RunnableFuture<V> extends Runnable, Future<V> {
2
3 void run();
4
5 }
於是,我們發現FutureTask類實際上是同時實現了Runnable和Future介面,由此才使得其具有Future和Runnable雙重特性。通過Runnable特性,可以作為Thread對象的target,而Future特性,使得其可以取得新創建線程中的call()方法的返回值。
執行下此程序,我們發現sum = 4950永遠都是最後輸出的。而「主線程for循環執行完畢..」則很可能是在子線程循環中間輸出。由CPU的線程調度機制,我們知道,「主線程for循環執行完畢..」的輸出時機是沒有任何問題的,那麼為什麼sum =4950會永遠最後輸出呢?
原因在於通過ft.get()方法獲取子線程call()方法的返回值時,當子線程此方法還未執行完畢,ft.get()方法會一直阻塞,直到call()方法執行完畢才能取到返回值。
上述主要講解了三種常見的線程創建方式,對於線程的啟動而言,都是調用線程對象的start()方法,需要特別注意的是:不能對同一線程對象兩次調用start()方法。
你好,本題已解答,如果滿意
請點右下角「採納答案」。