java多線程使用完成辦法。本站提示廣大學習愛好者:(java多線程使用完成辦法)文章只能為提供參考,不一定能成為您想要的結果。以下是java多線程使用完成辦法正文
以前沒有寫筆記的習氣,如今漸漸的發現及時總結是多麼的重要了,呵呵。雖然才大二,但是也快要畢業了,要加油了。
這一篇文章次要關於java多線程,次要還是以例子來驅動的。由於解說多線程的書籍和文章曾經很多了,所以我也不好意思多說,呵呵、大家可以去參考一些那些書籍。我這個文章次要關於實踐的一些問題。同時也算是我當前溫習的材料吧,。呵呵大家多多指教。
同時希望多結交一些技術上的冤家。謝謝。
----------------------------------------------------------------------------------------------------------------------------------------------------
java中的多線程
在java中要想完成多線程,有兩種手腕,一種是持續Thread類,另外一種是完成Runable接口。
關於直接承繼Thread的類來說,代碼大致框架是:
class 類名 extends Thread{
辦法1;
辦法2;
…
public void run(){
// other code…
}
屬性1;
屬性2;
…
}
先看一個復雜的例子:
/**
* @author Rollen-Holt 承繼Thread類,直接調用run辦法
* */
class hello extends Thread {
public hello() {
}
public hello(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "運轉 " + i);
}
}
public static void main(String[] args) {
hello h1=new hello("A");
hello h2=new hello("B");
h1.run();
h2.run();
}
private String name;
}
【運轉後果】:
A運轉 0
A運轉 1
A運轉 2
A運轉 3
A運轉 4
B運轉 0
B運轉 1
B運轉 2
B運轉 3
B運轉 4
我們會發現這些都是順序執行的,闡明我們的調用辦法不對,應該調用的是start()辦法。
當我們把下面的主函數修正為如下所示的時分:
public static void main(String[] args) {
hello h1=new hello("A");
hello h2=new hello("B");
h1.start();
h2.start();
}
然後運轉順序,輸入的能夠的後果如下:
A運轉 0
B運轉 0
B運轉 1
B運轉 2
B運轉 3
B運轉 4
A運轉 1
A運轉 2
A運轉 3
A運轉 4
由於需求用到CPU的資源,所以每次的運轉後果根本是都不一樣的,呵呵。
留意:雖然我們在這裡調用的是start()辦法,但是實踐上調用的還是run()辦法的主體。
那麼:為什麼我們不能直接調用run()辦法呢?
我的了解是:線程的運轉需求本地操作零碎的支持。
假如你檢查start的源代碼的時分,會發現:
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0 || this != me)
throw new IllegalThreadStateException();
group.add(this);
start0();
if (stopBeforeStart) {
stop0(throwableFromStop);
}
}
private native void start0();
留意我用白色加粗的那一條語句,闡明此處調用的是start0()。並且這個這個辦法用了native關鍵字,次關鍵字表示調用本地操作零碎的函數。由於多線程的完成需求本地操作零碎的支持。
但是start辦法反復調用的話,會呈現java.lang.IllegalThreadStateException異常。
經過完成Runnable接口:
大致框架是:
class 類名 implements Runnable{
辦法1;
辦法2;
…
public void run(){
// other code…
}
屬性1;
屬性2;
…
}
來先看一個小例子吧:
/**
* @author Rollen-Holt 完成Runnable接口
* */
class hello implements Runnable {
public hello() {
}
public hello(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "運轉 " + i);
}
}
public static void main(String[] args) {
hello h1=new hello("線程A");
Thread demo= new Thread(h1);
hello h2=new hello("線程B");
Thread demo1=new Thread(h2);
demo.start();
demo1.start();
}
private String name;
}
【能夠的運轉後果】:
線程A運轉 0
線程B運轉 0
線程B運轉 1
線程B運轉 2
線程B運轉 3
線程B運轉 4
線程A運轉 1
線程A運轉 2
線程A運轉 3
線程A運轉 4
關於選擇承繼Thread還是完成Runnable接口?
其實Thread也是完成Runnable接口的:
class Thread implements Runnable {
//…
public void run() {
if (target != null) {
target.run();
}
}
}
其實Thread中的run辦法調用的是Runnable接口的run辦法。不知道大家發現沒有,Thread和Runnable都完成了run辦法,這種操作形式其實就是代理形式。關於代理形式,我已經寫過一個小例子呵呵,大家有興味的話可以看一下:http://www.cnblogs.com/rollenholt/archive/2011/08/18/2144847.html
Thread和Runnable的區別:
假如一個類承繼Thread,則不合適資源共享。但是假如完成了Runable接口的話,則很容易的完成資源共享。
/**
* @author Rollen-Holt 承繼Thread類,不能資源共享
* */
class hello extends Thread {
public void run() {
for (int i = 0; i < 7; i++) {
if (count > 0) {
System.out.println("count= " + count--);
}
}
}
public static void main(String[] args) {
hello h1 = new hello();
hello h2 = new hello();
hello h3 = new hello();
h1.start();
h2.start();
h3.start();
}
private int count = 5;
}
【運轉後果】:
count= 5
count= 4
count= 3
count= 2
count= 1
count= 5
count= 4
count= 3
count= 2
count= 1
count= 5
count= 4
count= 3
count= 2
count= 1
大家可以想象,假如這個是一個買票零碎的話,假如count表示的是車票的數量的話,闡明並沒有完成資源的共享。
我們換為Runnable接口:
/**
* @author Rollen-Holt 承繼Thread類,不能資源共享
* */
class hello implements Runnable {
public void run() {
for (int i = 0; i < 7; i++) {
if (count > 0) {
System.out.println("count= " + count--);
}
}
}
public static void main(String[] args) {
hello he=new hello();
new Thread(he).start();
}
private int count = 5;
}
【運轉後果】:
count= 5
count= 4
count= 3
count= 2
count= 1
總結一下吧:
完成Runnable接口比承繼Thread類所具有的優勢:
1):合適多個相反的順序代碼的線程去處置同一個資源
2):可以防止java中的單承繼的限制
3):添加順序的強健性,代碼可以被多個線程共享,代碼和數據獨立。
所以,自己建議大家勁量完成接口。
/**
* @author Rollen-Holt
* 獲得線程的稱號
* */
class hello implements Runnable {
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
hello he = new hello();
new Thread(he,"A").start();
new Thread(he,"B").start();
new Thread(he).start();
}
}
【運轉後果】:
A
A
A
B
Thread-0
Thread-0
Thread-0
闡明假如我們沒有指定名字的話,零碎自動提供名字。
提示一下大家:main辦法其實也是一個線程。在java中所以的線程都是同時啟動的,至於什麼時分,哪個先執行,完全看誰先失掉CPU的資源。
在java中,每次順序運轉至多啟動2個線程。一個是main線程,一個是渣滓搜集線程。由於每當運用java命令執行一個類的時分,實踐上都會啟動一個JVM,每一個jVM實習在就是在操作零碎中啟動了一個進程。
判別線程能否啟動
/**
* @author Rollen-Holt 判別線程能否啟動
* */
class hello implements Runnable {
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
hello he = new hello();
Thread demo = new Thread(he);
System.out.println("線程啟動之前---》" + demo.isAlive());
demo.start();
System.out.println("線程啟動之後---》" + demo.isAlive());
}
}
【運轉後果】
線程啟動之前---》false
線程啟動之後---》true
Thread-0
Thread-0
Thread-0
主線程也有能夠在子線程完畢之前完畢。並且子線程不受影響,不會由於主線程的完畢而完畢。
線程的強迫執行:
/**
* @author Rollen-Holt 線程的強迫執行
* */
class hello implements Runnable {
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
hello he = new hello();
Thread demo = new Thread(he,"線程");
demo.start();
for(int i=0;i<50;++i){
if(i>10){
try{
demo.join(); //強迫執行demo
}catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("main 線程執行-->"+i);
}
}
}
【運轉的後果】:
main 線程執行-->0
main 線程執行-->1
main 線程執行-->2
main 線程執行-->3
main 線程執行-->4
main 線程執行-->5
main 線程執行-->6
main 線程執行-->7
main 線程執行-->8
main 線程執行-->9
main 線程執行-->10
線程
線程
線程
main 線程執行-->11
main 線程執行-->12
main 線程執行-->13
...
線程的休眠:
/**
* @author Rollen-Holt 線程的休眠
* */
class hello implements Runnable {
public void run() {
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + i);
}
}
public static void main(String[] args) {
hello he = new hello();
Thread demo = new Thread(he, "線程");
demo.start();
}
}
【運轉後果】:(後果每隔2s輸入一個)
線程0
線程1
線程2
線程的中綴:
/**
* @author Rollen-Holt 線程的中綴
* */
class hello implements Runnable {
public void run() {
System.out.println("執行run辦法");
try {
Thread.sleep(10000);
System.out.println("線程完成休眠");
} catch (Exception e) {
System.out.println("休眠被打斷");
return; //前往到順序的調用途
}
System.out.println("線程正常終止");
}
public static void main(String[] args) {
hello he = new hello();
Thread demo = new Thread(he, "線程");
demo.start();
try{
Thread.sleep(2000);
}catch (Exception e) {
e.printStackTrace();
}
demo.interrupt(); //2s後中綴線程
}
}
【運轉後果】:
執行run辦法
休眠被打斷
在java順序中,只需前台有一個線程在運轉,整個java順序進程不會小時,所以此時可以設置一個後台線程,這樣即便java進程小時了,爾後台線程仍然可以持續運轉。
/**
* @author Rollen-Holt 後台線程
* */
class hello implements Runnable {
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + "在運轉");
}
}
public static void main(String[] args) {
hello he = new hello();
Thread demo = new Thread(he, "線程");
demo.setDaemon(true);
demo.start();
}
}
雖然有一個死循環,但是順序還是可以執行完的。由於在死循環中的線程操作曾經設置為後台運轉了。
線程的優先級:
/**
* @author Rollen-Holt 線程的優先級
* */
class hello implements Runnable {
public void run() {
for(int i=0;i<5;++i){
System.out.println(Thread.currentThread().getName()+"運轉"+i);
}
}
public static void main(String[] args) {
Thread h1=new Thread(new hello(),"A");
Thread h2=new Thread(new hello(),"B");
Thread h3=new Thread(new hello(),"C");
h1.setPriority(8);
h2.setPriority(2);
h3.setPriority(6);
h1.start();
h2.start();
h3.start();
}
}
【運轉後果】:
A運轉0
A運轉1
A運轉2
A運轉3
A運轉4
B運轉0
C運轉0
C運轉1
C運轉2
C運轉3
C運轉4
B運轉1
B運轉2
B運轉3
B運轉4
。但是請讀者不要誤以為優先級越高就先執行。誰先執行還是取決於誰先去的CPU的資源、
另外,主線程的優先級是5.
線程的禮讓。
在線程操作中,也可以運用yield()辦法,將一個線程的操作暫時交給其他線程執行。
/**
* @author Rollen-Holt 線程的優先級
* */
class hello implements Runnable {
public void run() {
for(int i=0;i<5;++i){
System.out.println(Thread.currentThread().getName()+"運轉"+i);
if(i==3){
System.out.println("線程的禮讓");
Thread.currentThread().yield();
}
}
}
public static void main(String[] args) {
Thread h1=new Thread(new hello(),"A");
Thread h2=new Thread(new hello(),"B");
h1.start();
h2.start();
}
}
A運轉0
A運轉1
A運轉2
A運轉3
線程的禮讓
A運轉4
B運轉0
B運轉1
B運轉2
B運轉3
線程的禮讓
B運轉4
同步和死鎖:
【問題引出】:比方說關於買票零碎,有上面的代碼:
/**
* @author Rollen-Holt
* */
class hello implements Runnable {
public void run() {
for(int i=0;i<10;++i){
if(count>0){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(count--);
}
}
}
public static void main(String[] args) {
hello he=new hello();
Thread h1=new Thread(he);
Thread h2=new Thread(he);
Thread h3=new Thread(he);
h1.start();
h2.start();
h3.start();
}
private int count=5;
}
【運轉後果】:
5
4
3
2
1
0
-1
這裡呈現了-1,顯然這個是錯的。,應該票數不能為負值。
假如想處理這種問題,就需求運用同步。所謂同步就是在一致時間段中只要有一個線程運轉,
其他的線程必需等到這個線程完畢之後才干持續執行。
【運用線程同步處理問題】
采用同步的話,可以運用同步代碼塊和同步辦法兩種來完成。
【同步代碼塊】:
語法格式:
synchronized(同步對象){
//需求同步的代碼
}
但是普通都把以後對象this作為同步對象。
比方關於下面的買票的問題,如下:
/**
* @author Rollen-Holt
* */
class hello implements Runnable {
public void run() {
for(int i=0;i<10;++i){
synchronized (this) {
if(count>0){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(count--);
}
}
}
}
public static void main(String[] args) {
hello he=new hello();
Thread h1=new Thread(he);
Thread h2=new Thread(he);
Thread h3=new Thread(he);
h1.start();
h2.start();
h3.start();
}
private int count=5;
}
【運轉後果】:(每一秒輸入一個後果)
5
4
3
2
1
【同步辦法】
也可以采用同步辦法。
語法格式為synchronized 辦法前往類型 辦法名(參數列表){
// 其他代碼
}
如今,我們采用同步辦法處理下面的問題。
/**
* @author Rollen-Holt
* */
class hello implements Runnable {
public void run() {
for (int i = 0; i < 10; ++i) {
sale();
}
}
public synchronized void sale() {
if (count > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count--);
}
}
public static void main(String[] args) {
hello he = new hello();
Thread h1 = new Thread(he);
Thread h2 = new Thread(he);
Thread h3 = new Thread(he);
h1.start();
h2.start();
h3.start();
}
private int count = 5;
}
【運轉後果】(每秒輸入一個)
5
4
3
2
1
提示一下,當多個線程共享一個資源的時分需求停止同步,但是過多的同步能夠招致死鎖。
此處羅列經典的消費者和消費者問題。
【消費者和消費者問題】
先看一段有問題的代碼。
class Info {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private String name = "Rollen";
private int age = 20;
}
/**
* 消費者
* */
class Producer implements Runnable{
private Info info=null;
Producer(Info info){
this.info=info;
}
public void run(){
boolean flag=false;
for(int i=0;i<25;++i){
if(flag){
this.info.setName("Rollen");
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
this.info.setAge(20);
flag=false;
}else{
this.info.setName("chunGe");
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
this.info.setAge(100);
flag=true;
}
}
}
}
/**
* 消費者類
* */
class Consumer implements Runnable{
private Info info=null;
public Consumer(Info info){
this.info=info;
}
public void run(){
for(int i=0;i<25;++i){
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(this.info.getName()+"<---->"+this.info.getAge());
}
}
}
/**
* 測試類
* */
class hello{
public static void main(String[] args) {
Info info=new Info();
Producer pro=new Producer(info);
Consumer con=new Consumer(info);
new Thread(pro).start();
new Thread(con).start();
}
}
【運轉後果】:
Rollen<---->100
chunGe<---->20
chunGe<---->100
Rollen<---->100
chunGe<---->20
Rollen<---->100
Rollen<---->100
Rollen<---->100
chunGe<---->20
chunGe<---->20
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
大家可以從後果中看到,名字和年齡並沒有關於。
那麼如何處理呢?
<!--[if !supportLists]-->1) <!--[endif]-->參加同步
<!--[if !supportLists]-->2) <!--[endif]-->參加等候和喚醒
先來看看參加同步會是如何。
class Info {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public synchronized void set(String name, int age){
this.name=name;
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
this.age=age;
}
public synchronized void get(){
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(this.getName()+"<===>"+this.getAge());
}
private String name = "Rollen";
private int age = 20;
}
/**
* 消費者
* */
class Producer implements Runnable {
private Info info = null;
Producer(Info info) {
this.info = info;
}
public void run() {
boolean flag = false;
for (int i = 0; i < 25; ++i) {
if (flag) {
this.info.set("Rollen", 20);
flag = false;
} else {
this.info.set("ChunGe", 100);
flag = true;
}
}
}
}
/**
* 消費者類
* */
class Consumer implements Runnable {
private Info info = null;
public Consumer(Info info) {
this.info = info;
}
public void run() {
for (int i = 0; i < 25; ++i) {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
this.info.get();
}
}
}
/**
* 測試類
* */
class hello {
public static void main(String[] args) {
Info info = new Info();
Producer pro = new Producer(info);
Consumer con = new Consumer(info);
new Thread(pro).start();
new Thread(con).start();
}
}
【運轉後果】:
Rollen<===>20
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
從運轉後果來看,紊亂的問題處理了,如今是Rollen 對應20,ChunGe關於100
,但是還是呈現了反復讀取的問題,也一定有反復掩蓋的問題。假如想處理這個問題,就需求運用Object類幫助了、
,我們可以運用其中的等候和喚醒操作。
要完成下面的功用,我們只需求修正Info類饑渴,在其中加上標志位,並且經過判別標志位完成等候和喚醒的操作,代碼如下:
class Info {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public synchronized void set(String name, int age){
if(!flag){
try{
super.wait();
}catch (Exception e) {
e.printStackTrace();
}
}
this.name=name;
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
this.age=age;
flag=false;
super.notify();
}
public synchronized void get(){
if(flag){
try{
super.wait();
}catch (Exception e) {
e.printStackTrace();
}
}
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(this.getName()+"<===>"+this.getAge());
flag=true;
super.notify();
}
private String name = "Rollen";
private int age = 20;
private boolean flag=false;
}
/**
* 消費者
* */
class Producer implements Runnable {
private Info info = null;
Producer(Info info) {
this.info = info;
}
public void run() {
boolean flag = false;
for (int i = 0; i < 25; ++i) {
if (flag) {
this.info.set("Rollen", 20);
flag = false;
} else {
this.info.set("ChunGe", 100);
flag = true;
}
}
}
}
/**
* 消費者類
* */
class Consumer implements Runnable {
private Info info = null;
public Consumer(Info info) {
this.info = info;
}
public void run() {
for (int i = 0; i < 25; ++i) {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
this.info.get();
}
}
}
/**
* 測試類
* */
class hello {
public static void main(String[] args) {
Info info = new Info();
Producer pro = new Producer(info);
Consumer con = new Consumer(info);
new Thread(pro).start();
new Thread(con).start();
}
}
【順序運轉後果】:
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
先在看後果就可以知道,之前的問題完全處理。
《完》
PS(寫在前面):
自己深知學的太差,所以希望大家能多多指點。另外,關於多線程其實有很多的知識,由於目前我也就知道的不太多,寫了一些常用的。雖然在操作零碎這門課上學了很多的線程和進程,比方銀裡手算法等等的,當前有時間在補充,大家有什麼好材料可以留個言,大家一同分享一下,謝謝了。