-------android培訓(xùn)java培訓(xùn)期待與您交流!----------
概述
進(jìn)程:是一個(gè)正在執(zhí)行中的程序,每一個(gè)進(jìn)程執(zhí)行都有一個(gè)執(zhí)行的順序狡门。該順序是一個(gè)執(zhí)行路徑陷寝,或者叫一個(gè)控制單元。一個(gè)進(jìn)程中至少有一個(gè)線程其馏。
線程:進(jìn)程中一個(gè)獨(dú)立的控制單元凤跑,線程控制著進(jìn)程的執(zhí)行。
多線程的意義:提高cpu的使用率叛复。從而提高了應(yīng)用程序的使用率仔引。
線程和線程共享"堆內(nèi)存和方法區(qū)內(nèi)存",棧是獨(dú)立的褐奥,一個(gè)線程一個(gè)棧
Java程序的運(yùn)行原理:java命令會(huì)啟動(dòng)java虛擬機(jī)咖耘,啟動(dòng)jvm等于啟動(dòng)了一個(gè)應(yīng)用程序,表示啟動(dòng)了一個(gè)進(jìn)程撬码。該進(jìn)程會(huì)自動(dòng)啟動(dòng)一個(gè)"主線程"儿倒,然后主線程去調(diào)用某個(gè)類的main方法。所以main方法運(yùn)行在主線程中呜笑。
創(chuàng)建線程
- JAVA已經(jīng)提供了對(duì)線程事物的描述的類即Thread類夫否。有兩種方式創(chuàng)建線程。
第一種方式:繼承Thread類蹈垢;復(fù)寫Thread類中的run方法(將自定義的代碼存在run方法慷吊,讓線程調(diào)用);調(diào)用線程的start方法曹抬,啟動(dòng)線程,調(diào)用run方法急鳄。
package com.sergio.thread;
/**
* Created by Sergio on 2014/12/8.
*/
public class ThreadDemo{
public static void main(String[] args) {
//創(chuàng)建線程任務(wù)對(duì)象
ThreadCreate td = new ThreadCreate();
//啟動(dòng)線程谤民,而不是直接調(diào)用run方法。線程的真真的執(zhí)行是由java線程調(diào)度機(jī)制完成的疾宏。
td.start();
for(int x = 0; x < 1000; x++)
{
System.out.println("Demo " + x);
}
}
}
//創(chuàng)建需要執(zhí)行人物的類张足,繼承之Thread類
class ThreadCreate extends Thread
{
//復(fù)寫Thread類中的run方法。定義自己的內(nèi)容
public void run(){
for(int x = 0; x < 100; x++){
System.out.println("Heall word! " + x);
}
}
}
run和start的特點(diǎn)
- run:僅僅是對(duì)象調(diào)用方法坎藐,并沒有運(yùn)行線程(Thread類的run方法用于存儲(chǔ)線程要運(yùn)行的代碼)为牍。
- start:開啟線程并執(zhí)行該線程的run方法哼绑。
練習(xí)
package com.sergio.thread;
/**
* Created by Sergio on 2014/12/8.
* 創(chuàng)建兩個(gè)線程與main線程交替運(yùn)行
*/
public class ThreadDemo1 {
public static void main(String[] args) {
//創(chuàng)建兩個(gè)線程對(duì)象
ThreadCreate1 td1 = new ThreadCreate1("線程一");
ThreadCreate1 td2 = new ThreadCreate1("線程二");
//啟動(dòng)兩個(gè)線程
td1.start();
td2.start();
for(int x =0; x < 60; x++)
{
System.out.println("main run..." + x);
}
}
}
//創(chuàng)建執(zhí)行任務(wù)的線程繼承thread類
class ThreadCreate1 extends Thread
{
private String name;
ThreadCreate1(String name)
{
this.name = name;
}
//復(fù)寫run方法
public void run(){
for(int x = 0; x < 60; x++)
{
System.out.println(name + "run..." + x);
}
}
}
多線程聲明周期
-
如圖:
新建:用new語(yǔ)句創(chuàng)建完成
就緒:執(zhí)行start后
運(yùn)行:占用cpu時(shí)間
阻塞:執(zhí)行了wait語(yǔ)句、執(zhí)行了sleep語(yǔ)句和等待某個(gè)對(duì)象鎖碉咆,等待輸入的場(chǎng)合
消亡:退出run()方法
獲取多線程對(duì)象及名稱
- 線程都有自己的默認(rèn)名稱:
Thread-編號(hào)
該編號(hào)從0開始抖韩。
線程的幾個(gè)方法:
1.static Thread currentThread():獲取當(dāng)前線程對(duì)象。
2.getName():獲取線程的名稱疫铜。
3.設(shè)置線程名稱:setName或者構(gòu)造函數(shù)茂浮。
package com.sergio.thread;
/**
* Created by Sergio on 2014/12/8.
*/
public class ThreadDemo1 {
public static void main(String[] args) {
//創(chuàng)建兩個(gè)線程對(duì)象
ThreadCreate1 td1 = new ThreadCreate1("線程一");
ThreadCreate1 td2 = new ThreadCreate1("線程二");
//啟動(dòng)兩個(gè)線程
td1.start();
td2.start();
}
}
//創(chuàng)建執(zhí)行任務(wù)的線程繼承thread類
class ThreadCreate1 extends Thread
{
ThreadCreate1(String name)
{
//給線程命名
super(name);
}
//復(fù)寫run方法
public void run(){
for(int x = 0; x < 60; x++)
{
//Thread.currentThread()通過此靜態(tài)方法獲取當(dāng)前線程對(duì)象
System.out.println(Thread.currentThread().getName() + "run..." + x);
}
}
}
創(chuàng)建線程
創(chuàng)建線程的第二種方式:實(shí)現(xiàn)Runnable接口。
步驟:定義類實(shí)現(xiàn)Runnable接口壳咕;覆蓋Runnable接口中的run方法席揽;通過Thread類建立線程對(duì)象;將Runable接口的子類對(duì)象作為實(shí)際參數(shù)傳遞給Thread類的構(gòu)造函數(shù)谓厘;調(diào)用Thread類的start方法開啟線程并調(diào)用Runnable接口子類的run方法幌羞。
package com.sergio.thread;
import sun.security.krb5.internal.Ticket;
/**
* Created by Sergio on 2014/12/9.
*/
public class RunnableThreadDemo {
public static void main(String[] args) {
//創(chuàng)建賣票類實(shí)例對(duì)象
RunnableThreadCreate t = new RunnableThreadCreate();
//將通過Thread類建立線程對(duì)象;將Runnable接口的子類對(duì)象作為實(shí)際參數(shù)傳遞給Thread類的構(gòu)造函數(shù)
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
//啟動(dòng)線程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//實(shí)現(xiàn)Runnable接口竟稳,模擬賣票功能類
class RunnableThreadCreate implements Runnable
{
private int ticket = 100;
@Override
public void run() {
while(true)
{
if(ticket > 0)
{
System.out.println(Thread.currentThread().getName() + "售出票號(hào)" + ticket--);
}
}
}
}
實(shí)現(xiàn)方式和繼承方式有什么區(qū)別:
實(shí)現(xiàn)方式的好處:避免了單繼承的局限性新翎,在定義線程時(shí),建議使用實(shí)現(xiàn)方式住练。
區(qū)別:
繼承Thread:線程代碼存放Thread子類run方法中地啰。
實(shí)現(xiàn)Runnable:線程代碼存在接口的子類的run方法
多線程安全
先來(lái)看一個(gè)例子:
package com.sergio.thread;
/**
* Created by Sergio on 2014/12/9.
*/
public class TreadSafeDemo {
public static void main(String[] args) {
//
ThreadSafeCreateDemo ts = new ThreadSafeCreateDemo();
Thread t1 = new Thread(ts, "t1");
t1.start();
Thread t2 = new Thread(ts, "t2");
t2.start();
}
}
class ThreadSafeCreateDemo implements Runnable
{
//成員變量s,作為累加變量
private int s = 0;
@Override
public void run()
{
//計(jì)算1-10的相加合
for(int i = 0; i < 10;i++)
{
s += i;
}
System.out.println(Thread.currentThread().getName() + ", s = " + s);
}
}
打印的結(jié)果為:
t1, s = 45
t2, s = 90
這是為何讲逛?這是因?yàn)楣蚕砹送粋€(gè)對(duì)象的的成員變量s亏吝,兩個(gè)線程同時(shí)對(duì)其進(jìn)行操作,所以產(chǎn)生了上述為題盏混。這個(gè)結(jié)果造成的現(xiàn)象稱為線程不安全問題蔚鸥。
解決方法:對(duì)多條線程操作共享數(shù)據(jù)的語(yǔ)句,只能讓一個(gè)線程都執(zhí)行完许赃,在執(zhí)行過程過中止喷,其他線程不可以參與執(zhí)行。
Java對(duì)于多線程的安全問題提供了解決方式:同步代碼塊
synchronized(對(duì)象)
{
需要被同步的代碼
}
對(duì)于上述的例子修改如下:
package com.sergio.thread;
/**
* Created by Sergio on 2014/12/9.
*/
public class TreadSafeDemo {
public static void main(String[] args) {
ThreadSafeCreateDemo ts = new ThreadSafeCreateDemo();
Thread t1 = new Thread(ts, "t1");
t1.start();
Thread t2 = new Thread(ts, "t2");
t2.start();
}
}
class ThreadSafeCreateDemo implements Runnable
{
//成員變量s混聊,作為累加變量
private int s = 0;
//增加關(guān)鍵字synchronized來(lái)給對(duì)象加鎖使代碼同步安全問題得到解決弹谁,但是會(huì)帶來(lái)效率變慢問題
@Override
public synchronized void run()
{
//計(jì)算1-10的相加合
synchronized (this){
for(int i = 0; i < 10;i++)
{
s += i;
}
}
System.out.println(Thread.currentThread().getName() + ", s = " + s);
s = 0;
}
}
synchronized同步代碼塊如同給對(duì)象加鎖,持有鎖的線程可以在同步中執(zhí)行句喜,沒有持有鎖的線程即使獲取了cpu的執(zhí)行權(quán)预愤,也進(jìn)不到同步代碼塊中,因?yàn)闆]有獲取鎖咳胃。
同步的前提:
- 必須要有兩個(gè)或者兩個(gè)以上的線程
- 必須是多個(gè)線程使用同一個(gè)鎖
必須保證同步中只能有一個(gè)線程在運(yùn)行
好處和弊端:
- 好處:解決了多線程的安全問題
- 弊端:多個(gè)線程需要判斷鎖植康,較為消耗資源。
同步有兩種表示:同步代碼塊和同步函數(shù)展懈。
如何找同步:
- 明確哪些代碼是多線程運(yùn)行代碼销睁。
- 明確共享數(shù)據(jù)赊级。
- 明確多線程運(yùn)行代碼中那些語(yǔ)句是操作共享數(shù)據(jù)的唯袄。
同步函數(shù)例子:
package com.sergio.thread;
/**
* Created by Sergio on 2014/12/9.
* 兩個(gè)客戶同時(shí)往一個(gè)賬戶中存錢,每次100聊倔,分別分三次寸
*/
public class ThreadBankDemo {
public static void main(String[] args) {
Cus c = new Cus();
Thread t1 = new Thread(c, "t1");
t1.start();
Thread t2 = new Thread(c, "t2");
t2.start();
}
}
//創(chuàng)建銀行類
class Bank
{
//存錢的成員變量
private int sum;
//同步代碼函數(shù)茅诱。存錢函數(shù)
public synchronized void add(int n)
{
sum = sum + n;
System.out.println(sum);
}
}
//客戶存錢函數(shù)類實(shí)現(xiàn)多線程接口
class Cus implements Runnable
{
//創(chuàng)建銀行類對(duì)象
Bank b = new Bank();
//需要多線程執(zhí)行的代碼
@Override
public void run() {
for(int x = 0; x < 3; x++)
{
//調(diào)用存錢的方法
b.add(100);
}
}
}
同步函數(shù)鎖
- 函數(shù)需要被對(duì)象調(diào)用,函數(shù)都有一個(gè)所屬對(duì)象引用檩赢,就是this吕嘀。所以同步函數(shù)使用的鎖是this。
public void run()
{
//this當(dāng)前運(yùn)行的對(duì)象引用
synchronized (this){
//計(jì)算1-10的相加合
for(int i = 0; i < 10;i++)
{
s += i;
}
System.out.println(Thread.currentThread().getName() + ", s = " + s);
s = 0;
}
}
靜態(tài)同步函數(shù)
- 靜態(tài)方法中不可以定義this贞瞒,沒對(duì)象偶房。
- 靜態(tài)進(jìn)內(nèi)存時(shí),內(nèi)存中沒有本類對(duì)象军浆,但是一定有該類對(duì)應(yīng)的字節(jié)碼文件對(duì)象棕洋。
類名.class
該對(duì)象的類型是Class。 - 靜態(tài)的同步方法乒融,使用的鎖是該方法所在類的字節(jié)碼文件對(duì)象掰盘。也就是
類名.class
。
//靜態(tài)的 SinglePattern2實(shí)例對(duì)象變量
private static SinglePattern2 instance = null;
//私有構(gòu)造函數(shù)
private SinglePattern2(){}
public static SinglePattern2 getInstance()
{
if(instance == null)
{
//靜態(tài)同步函數(shù)調(diào)用方式類名.class
synchronized (SinglePattern2.class)
{
if(instance == null)
{
instance = new SinglePattern2();
}
}
}
return instance;
}
死鎖
- 概念:同步中嵌套同步但是鎖對(duì)象不同赞季。
package com.sergio.thread;
/**
* 構(gòu)建鎖的機(jī)制的例子
* Created by Sergio on 2014/12/10.
*/
public class ThreadDeadLock2 {
public static void main(String[] args) {
Test test = new Test(false);
Test test2 = new Test(true);
Thread t = new Thread(test);
Thread t2 = new Thread(test2);
t.start();
t2.start();
}
}
class Test implements Runnable
{
//設(shè)置運(yùn)行標(biāo)志
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
//重寫run方法愧捕,運(yùn)行鎖的機(jī)制。前提:同步中包含同步申钩,但是鎖的對(duì)象引用次绘。
@Override
public void run() {
if(flag)
{
//鎖locka對(duì)象的引用
synchronized (Lock.locka)
{
System.out.println("if locka");
//鎖lock對(duì)象的引用
synchronized (Lock.lockb)
{
System.out.println("if lockb");
}
}
}
else
{
//鎖lockb對(duì)象的引用
synchronized (Lock.lockb)
{
System.out.println("else lockb");
//鎖locka對(duì)象的引用
synchronized (Lock.locka)
{
System.out.println("else locka");
}
}
}
}
}
//創(chuàng)建鎖類
class Lock
{
//構(gòu)建兩個(gè)鎖對(duì)象引用
static Object locka = new Object();
static Object lockb = new Object();
}
線程間通信
- 多個(gè)線程在操作同一個(gè)資源,但是操作的動(dòng)作不同撒遣。
package com.sergio.thread;
/**
* Created by Sergio on 2014/12/11.
*/
public class ThreadDemo2 {
public static void main(String[] args) {
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
//需要操作的資源類
class Res
{
String name;
String sex;
}
//輸入操作線程類
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r = r;
}
//存資源運(yùn)行方法
@Override
public void run() {
int x = 0;
while(true)
{
//同步運(yùn)行時(shí)的對(duì)象鎖
synchronized (r) {
if (x == 0) {
r.name = "mike";
r.sex = "man";
} else {
r.name = "麗麗";
r.sex = "女";
}
}
//改變運(yùn)行條件標(biāo)志值
x = (x + 1) % 2;
}
}
}
//輸出操作線程類
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
//打印資源方式
@Override
public void run() {
while(true)
{
//同步運(yùn)行時(shí)的同步鎖
synchronized (r) {
System.out.println(r.name + "------" + r.sex);
}
}
}
}
- 等待喚醒機(jī)制
package com.sergio.thread;
/**
* Created by Sergio on 2014/12/11.
*/
public class ThreadDemo2 {
public static void main(String[] args) {
Res r = new Res();
//啟動(dòng)兩個(gè)線程
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
//需要操作的資源類
class Res {
private String name;
private String sex;
//設(shè)置運(yùn)行時(shí)的鎖標(biāo)志
private boolean flag = false;
//同步輸入鎖
public synchronized void set(String name, String sex) {
if (flag) {
try {
//讓同步線程對(duì)象等待邮偎,并撲獲異常。
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
//改變同步鎖運(yùn)行時(shí)的標(biāo)志值
flag = true;
//喚醒同步線程池中的鎖义黎。也就是res對(duì)象
this.notify();
}
//同步輸出鎖
public synchronized void out() {
if (!flag) {
try {
//讓同步線程對(duì)象等待禾进,并撲獲異常。
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name + "--------" + sex);
//改變同步鎖運(yùn)行時(shí)的標(biāo)志值
flag = false;
//喚醒同步線程池中的鎖廉涕。也就是res對(duì)象
this.notify();
}
}
//輸入操作線程類
class Input implements Runnable {
private Res r;
Input(Res r) {
this.r = r;
}
//存資源運(yùn)行方法
@Override
public void run() {
int x = 0;
while (true) {
//交替輸入信息
if (x == 0) {
r.set("make", "man");
} else {
r.set("麗麗", "女");
}
//設(shè)置交替輸入方法運(yùn)行的標(biāo)志
x = (x + 1) % 2;
}
}
}
//輸出操作線程類
class Output implements Runnable {
private Res r;
Output(Res r) {
this.r = r;
}
//打印資源方式
@Override
public void run() {
while (true) {
r.out();
}
}
}
- wait();notify();notifyall();都是用在同步中泻云,因?yàn)橐獙?duì)持有監(jiān)視器(對(duì)象鎖)的線程操作,而同步中才有鎖火的。
- 這些操作方法定義在Object類中壶愤。因?yàn)檫@些方法在操作同步中線程時(shí),都必須要標(biāo)識(shí)他們所操作線程只有的鎖馏鹤,只有同一個(gè)鎖上的被等待線程,可以被同一個(gè)鎖上notify喚醒娇哆。不可以對(duì)不同鎖中的線程進(jìn)行喚醒湃累。也就是說勃救,等待和喚醒必須是同一個(gè)鎖。而鎖可以是任意對(duì)象治力,所以可以被任意對(duì)象調(diào)用的方法定義Object類中蒙秒。
生產(chǎn)者消費(fèi)模式
示例1:(JDK1.5以前方式書寫)
package com.sergio.thread;
/**
* Created by Sergio on 2015/1/3.
*/
class ProducerConsumerDemo {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/*
對(duì)于多個(gè)生產(chǎn)者和消費(fèi)者。
為什么要定義while判斷標(biāo)記宵统。
原因:讓被喚醒的線程再一次判斷標(biāo)記晕讲。
為什么定義notifyAll,
因?yàn)樾枰獑拘褜?duì)方線程马澈。
因?yàn)橹挥胣otify瓢省,容易出現(xiàn)只喚醒本方線程的情況。導(dǎo)致程序中的所有線程都等待痊班。
*/
//定義資源類
class Resource {
//定義名字變量
private String name;
//計(jì)數(shù)基數(shù)
private int count = 1;
//線程運(yùn)行標(biāo)記
private boolean flag = false;
//設(shè)置生產(chǎn)者模式方法
public synchronized void set(String name) {
while (flag)
try {
//當(dāng)前線程等待
this.wait();
} catch (Exception e)
{}
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "...生產(chǎn)者.." + this.name);
//更改標(biāo)記值
flag = true;
//喚醒剩余全部鎖
this.notifyAll();
}
//設(shè)置消費(fèi)者模式方法
public synchronized void out() {
while (!flag)
try {
this.wait();
} catch (Exception e)
{}
System.out.println(Thread.currentThread().getName() + "...消費(fèi)者........." + this.name);
flag = false;
this.notifyAll();
}
}
class Producer implements Runnable {
private Resource res;
Producer(Resource res) {
this.res = res;
}
public void run() {
while (true) {
res.set("+商品+");
}
}
}
class Consumer implements Runnable {
private Resource res;
Consumer(Resource res) {
this.res = res;
}
public void run() {
while (true) {
res.out();
}
}
}
示例2:(JDK1.5版本新特性勤婚。)
- 將同步Synchronized替換成顯式Lock操作。
- 將Object中的wait涤伐,notify馒胆,notifyAll替換成了Condition對(duì)象,該對(duì)象可以用Lock鎖來(lái)進(jìn)行獲取凝果。
Lock:替換了Synchronized封裝成了lock祝迂、unlock、newCondition()器净。
Condition:替換了Object型雳、wait、notify掌动、notifyAll封裝成了await()四啰、signal()、signalAll().
package com.sergio.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 生產(chǎn)者消費(fèi)者模式
* Created by Sergio on 2015/1/3.
*/
class ProducerConsumerDemo {
public static void main(String[] args) {
Resource r = new Resource();
Producer2 pro = new Producer2(r);
Consumer2 con = new Consumer2(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//定義資源類
class Resource {
//定義名稱
private String name;
//定義計(jì)數(shù)基數(shù)
private int count = 1;
//定義線程運(yùn)行標(biāo)志
private boolean flag = false;
//定義鎖lock對(duì)象變量
private Lock lock = new ReentrantLock();
//定義狀態(tài)condition_pro對(duì)象變量
private Condition condition_pro = lock.newCondition();
//定義狀態(tài)condition_con對(duì)象變量
private Condition condition_con = lock.newCondition();
//定義生產(chǎn)者的方法
public void set(String name) throws InterruptedException {
//獲取當(dāng)前鎖鎖住對(duì)象
lock.lock();
try {
while (flag) {
//使生產(chǎn)者對(duì)象鎖等待粗恢,在函數(shù)上聲明異常類
condition_pro.await();
}
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "生產(chǎn)者" + this.name);
flag = true;
//喚醒消費(fèi)者對(duì)象鎖
condition_con.signal();
} finally {
//釋放鎖的動(dòng)作一定要執(zhí)行柑晒。
lock.unlock();
}
}
//消費(fèi)者方法
public synchronized void out() throws InterruptedException {
lock.lock();
try {
while (!flag) {
//使消費(fèi)者對(duì)象鎖等待。在函數(shù)上聲明異常類
condition_con.await();
}
System.out.println(Thread.currentThread().getName() + "...消費(fèi)者..." + this.name);
flag = false;
//喚醒生產(chǎn)者對(duì)象鎖
condition_pro.signal();
} finally {
lock.unlock();
}
}
}
//定義生產(chǎn)者
class Producer2 implements Runnable {
private Resource res;
Producer2(Resource res) {
this.res = res;
}
public void run() {
while (true) {
try {
res.set("商品");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//定義消費(fèi)者
class Consumer2 implements Runnable {
private Resource res;
Consumer2(Resource res) {
this.res = res;
}
@Override
public void run() {
while (true) {
try {
res.out();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
停止線程
stop方法已經(jīng)過時(shí)眷射。
- 停止方法1:run方法結(jié)束匙赞。開啟多線程運(yùn)行,運(yùn)行代碼通常是循環(huán)結(jié)構(gòu)妖碉。只要控制住循環(huán)涌庭,就可以讓run方法結(jié)束,也就是線程結(jié)束欧宜。
package com.sergio.thread;
/**
* 利用循環(huán)結(jié)構(gòu)結(jié)束線程
* Created by Sergio on 2015/1/3.
*/
class StopThread {
public static void main(String[] args) {
StopThreadDemo sp = new StopThreadDemo();
Thread t1 = new Thread(sp);
Thread t2 = new Thread(sp);
t1.start();
t2.start();
//計(jì)數(shù)基數(shù)
int num = 0;
while (true)
{
if(num++ == 30)
{
//num為30后調(diào)用changeFlag方法改變標(biāo)記值
sp.changeFlag();
break;
}
System.out.println(Thread.currentThread().getName() + "....." + num);
}
}
}
class StopThreadDemo implements Runnable
{
//設(shè)置循環(huán)運(yùn)行標(biāo)記值
private boolean flag = true;
@Override
public void run()
{
while(flag)
{
//打印當(dāng)然運(yùn)行的線程名字
System.out.println(Thread.currentThread().getName() + "....run");
}
}
//改變標(biāo)記值為false
public void changeFlag()
{
flag = false;
}
}
- 停止方法2:使用interrupt()方法坐榆。結(jié)束線程的凍結(jié)狀態(tài)(sleep、wait冗茸、join方法)席镀,使線程回到運(yùn)行狀態(tài)中來(lái)匹中。也就是靠異常機(jī)制來(lái)結(jié)束線程。
package com.sergio.thread;
/**
* 打斷線程凍結(jié)狀態(tài):當(dāng)沒有指定的方式讓凍結(jié)的線程恢復(fù)到運(yùn)行狀態(tài)時(shí)豪诲,這時(shí)需要對(duì)凍結(jié)進(jìn)行清除顶捷。
* 強(qiáng)制讓線程恢復(fù)到運(yùn)行狀態(tài)中來(lái)。改變標(biāo)記讓線程運(yùn)行
* Created by Sergio on 2015/1/5.
*/
public class StopThreadDemo2 {
public static void main(String[] args) {
StopThread2 sp = new StopThread2();
Thread t1 = new Thread(sp);
Thread t2 = new Thread(sp);
t1.start();
t2.start();
//計(jì)數(shù)基數(shù)
int num = 0;
while (true) {
if (num++ == 60) {
//打斷t1屎篱、t2線程的凍結(jié)狀態(tài)服赎,讓線程恢復(fù)到運(yùn)行狀態(tài)
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName() + "....." + num);
}
}
}
class StopThread2 implements Runnable {
private boolean flag = true;
@Override
public synchronized void run() {
while (flag) {
try {
//凍結(jié)狀態(tài)
wait();
} catch (InterruptedException e) {
//異常處理方法
System.out.println(Thread.currentThread().getName() + "中斷異常");
flag = false;
}
System.out.println(Thread.currentThread().getName() + "...run");
}
}
}
守護(hù)線程
線程分類為:用戶線程和守護(hù)線程。守護(hù)線程可以理解為后臺(tái)線程交播,我們看到的都是用戶線程重虑。守護(hù)線程開啟后,同用戶線程共同搶奪cpu執(zhí)行權(quán)堪侯。只是在結(jié)束時(shí)不一樣嚎尤,守護(hù)線程在所有用戶線程結(jié)束后,會(huì)自動(dòng)結(jié)束伍宦。像是守護(hù)依賴于用戶線程芽死。比如說:輸入動(dòng)作線程結(jié)束后輸出動(dòng)作線程自動(dòng)結(jié)束。java的垃圾回收器次洼。
語(yǔ)法:
setDaemon(boolean b)
关贵。true為開啟。
注意:該方法必須在啟動(dòng)線程前調(diào)用卖毁。
join方法
- 也就是搶奪cpu的執(zhí)行權(quán)揖曾。可以用來(lái)臨時(shí)加入線程執(zhí)行亥啦。
- 特點(diǎn):當(dāng)a線程執(zhí)行到了b線程的join方法炭剪,a就會(huì)等待,等b線程都執(zhí)行完翔脱,a才會(huì)從凍結(jié)狀態(tài)恢復(fù)到運(yùn)行狀態(tài)執(zhí)行奴拦。
- 語(yǔ)法:
t1.join();
線程優(yōu)先級(jí)
- 目前CPU使用權(quán)爭(zhēng)奪有兩種調(diào)度模型:分時(shí)調(diào)度模型和搶占式調(diào)度模型,Java 使用搶占式調(diào)度模型届吁。
1. 分時(shí)調(diào)度模型:所有線程輪流使用CPU 的使用權(quán)错妖,平均分配每個(gè)線程占用CPU 的時(shí)間片
2. 搶占式調(diào)度模型:優(yōu)先讓優(yōu)先級(jí)高的線程使用CPU,如果線程的優(yōu)先級(jí)相同疚沐,那么會(huì)隨機(jī)選擇一個(gè)暂氯,優(yōu)先級(jí)高的線程獲取的CPU 時(shí)間片相對(duì)多一些。 - 線程優(yōu)先級(jí)為了:MAX_PRIORITY(最高級(jí)為10)亮蛔,MIN_PRIORITY(最低級(jí)為1)痴施,NORM_PRIORITY(默認(rèn)及為5)。優(yōu)先級(jí)為:1-10。優(yōu)先級(jí)高的線程只能說明能搶占的cpu執(zhí)行權(quán)較多些晾剖,執(zhí)行的時(shí)間較多些锉矢。
- 線程啟動(dòng)后不能再次設(shè)置優(yōu)先級(jí)梯嗽。必須在線程啟動(dòng)前設(shè)置優(yōu)先級(jí)齿尽。
package com.sergio.thread;
/**
* 設(shè)置線程優(yōu)先級(jí)
* Created by Sergio on 2015/1/5.
*/
public class ThreadPriorityDemo {
public static void main(String[] args) {
ThreadPriority tp = new ThreadPriority();
Thread t1 = new Thread(tp, "t1");
Thread t2 = new Thread(tp, "t2");
//設(shè)置t1線程為10等級(jí)
t1.setPriority(Thread.MAX_PRIORITY);
t1.start();
//設(shè)置線程t2為1等級(jí)
t2.setPriority(Thread.MIN_PRIORITY);
t2.start();
}
}
class ThreadPriority implements Runnable
{
public void run()
{
for(int i = 0; i < 100; i++)
{
System.out.println(Thread.currentThread().getName() + ",,," + i);
}
}
}
yield方法
- 跟sleep方法類似,只是不能由用戶指定暫停多長(zhǎng)時(shí)間灯节,減緩線程的運(yùn)行頻率循头,并且yield方法只能讓同優(yōu)先級(jí)的線程有執(zhí)行機(jī)會(huì)。
class ThreadPriority implements Runnable
{
public void run()
{
for(int i = 0; i < 100; i++)
{
System.out.println(Thread.currentThread().getName() + ",,," + i);
if(i % 10 == 0)
{
System.out.println("--------");
//暫停當(dāng)前正在執(zhí)行的線程炎疆,并執(zhí)行其他同等優(yōu)先級(jí)的線程
//當(dāng)i為能被10整除時(shí)卡骂,該線程就會(huì)把CPU時(shí)間讓掉,讓其他或者自己的線程執(zhí)行
Thread.yield();
}
}
}
}