1.線程的基本操作
線程共有5個狀態:
NEW(新建狀態)
RUNNABLE(可運行態)
TERMINATED(線程終止)
BLOCKED (阻塞) ,進入同步塊時申請監視器導致的阻塞
WAITING (無限等待)
TIMED_WAITING(有限等待)
1.1新建線程
常見有兩種創建線程的方法:
1)繼承Thread類重寫run()方法。
2)繼承Runnable接口。在構造時傳入具體實現對象。
如果沒有傳遞target實例,那麼run方法內部就不會執行任何邏輯。
如果傳遞了tartget,那麼run方法便會執行target實例中的run方法。
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
.....
/* What will be run. */
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
要注意的是,如果沒有執行start()方法開啓線程,而是直接在主線程中調用一個線程的run()方法,結果只會在當前線程調用run()方法,而並沒有實質性地開啓新線程。
1.2 終止線程——stop()
Thread.stop()是一個被棄用的方法。主要原因是線程的突然終止會導致數據的不一致。太過於粗暴。
@Deprecated
public final void stop() {
....
}
1.3 中斷線程——interrupt()
public void Thread.interrupt() //中斷線程
public boolean Thread.isInterrupted() //判斷是否被中斷
public static boolean Thread.interrupted() //判斷是否被中斷,並清除當前中斷狀態
public void run(){
while(true){
//doSomething.....
}
}
t1.interrupt(); //發出中斷命令,而並未做出響應。所以while循環會繼續執行下去
/////////////////////////////////////////////////////////////////////////////////
public void run(){
while(true){
if(Thread.currentThread.isInterrupted()){
System.out.println("Interruted!");
break;
}
//doSomething...
}
}
t1.interrupt(); //發出中斷命令,while循環中會判斷出現中斷,退出循環
////////////////////////////////////////////////////////////////////////////
但若是doSomething中出現異常時候,要防範拋出異常時,中斷標記位會被清空。要重新中斷
public void run(){
while(true){
if(Thread.currentThread.isInterrupted()){
System.out.println("Interruted!");
break;
}
try{
Thread.sleep(2000);
}catch(InterruptedException e){
System.out.println("Interrupted When Sleep");
//由於拋出異常後會清除中斷標記,所以再次設置中斷標記,以被if檢測到
Thread.currentThread().interrupt()
}
//doSomething...
}
}
1.4 掛起(suspend) 和繼續執行(resume) 線程
這兩個操作也是不被推薦使用的。兩個線程無法控制順序時,當resume()在suspend()執行之前就已經執行,所以此後被掛起的線程便再也不能被喚醒了,而suspend()是不會釋放鎖的,導致等待在這個鎖上的其他線程會持續等待下去。
1.5 等待線程結束(join)和謙讓(yeild)
join() 表示等待前一個線程執行完畢後再執行,也有叫“線程插隊”
public class JoinMain{
public volatile static int i = 0;
public static class AddThread extends Thread{
@Override
public void run() {
for(i=0; i < 10000000; i++);
}
}
public static void main(String args[]) throws InterruptedException{
AddThread at = new AddThread();
at.start();
at.join(); //主線程會等待at線程執行完畢後再一起走
System.out.println(i);
}
}
/////////////////////////////////////////////////
join的本質:
while(isAlive()){
wait(0);
}
線程執行完畢會調用notifyAll()通知等待的所有線程
yeild會讓出時間片,但不是白給,而是再和別的線程競爭。”再給對方一次競爭機會“。
public final void join() throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException//一般用於測試目的
2. 守護線程&線程優先級
當一個Java應用內,只有守護線程時,JVM就會自然退出。
java 中的線程優先級的範圍是1~10,默認的優先級是5。高優先級的線程更容易在競爭中獲勝。
package Thread;
/**
* 後臺線程
* @author liq
*
*/
public class DamonThread {
class Damon implements Runnable{
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" is running");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Damon damon = new DamonThread().new Damon();
Thread damonThread = new Thread(damon, "後臺線程");
damonThread.setPriority(10); //在start之前設置優先級
damonThread.setDaemon(true);
System.out.println("'damonThread' is a Damon Thread?"+damonThread.isDaemon());
damonThread.start();
for(int i=0; i<10; i++) {
try {
Thread.sleep(1000);
System.out.println("Main Thrad is Running"+i);
if (i == 9) {
System.out.println("Main Thrad is dead");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3. 基本的線程同步操作——synchronized關鍵字&wait(),notify()
3.1 synchronized線程同步
-指定加鎖對象:對給定對象加鎖,進入同步代碼前要獲得給定對象的鎖。
-直接作用於實例方法:相當於對當前實例加鎖,進入同步代碼前要獲得當前實例的鎖。
-直接作用於靜態方法:相當於對當前類加鎖,進入同步代碼前要獲得當前類的鎖。
下面進行
/**
* synchronized代碼塊
* @author liq
*
*/
public class SynchronizedBlock {
public static void main(String[] args) {
Ticket ticketTask = new Ticket();
new Thread(ticketTask,"售票窗口1").start();
new Thread(ticketTask,"售票窗口2").start();
new Thread(ticketTask,"售票窗口3").start();
new Thread(ticketTask,"售票窗口4").start();
}
}
class Ticket implements Runnable{
private int ticketsNum = 10; //總共十張票
Object lock = new Object();
@Override
public void run() {
while(true) {
synchronized (lock) {
try {
if (ticketsNum > 0) {
System.out.println(Thread.currentThread().getName()+"售票,現存:"+ --ticketsNum);
Thread.sleep(1000);
}else {
System.out.println("票售完了!");
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
鎖靜態方法,就是對當前類加鎖
class Ticket implements Runnable{
private static int ticketsNum = 10; //總共十張票
Object lock = new Object();
@Override
public void run() {
sellTickt();
}
public static synchronized void sellTickt() { //鎖靜態方法,就是對當前類加鎖
while(true) {
try {
if (ticketsNum > 0) {
System.out.println(Thread.currentThread().getName()+"售票,現存:"+ --ticketsNum);
Thread.sleep(1000);
}else {
System.out.println("票售完了!");
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.2 wait()和notify()實現線程同步
需要注意:
1)wait和notify方法必須在同步代碼塊中執行,即在執行前需要拿到鎖。
2)wait()會釋放鎖。
3)notify也會釋放鎖。
notify()是在同步隊列中隨機喚醒一個線程
notifyAll()是喚醒全部開始競爭鎖。
實現生產者消費者例子:
/**
* This demon shows the co-operation between two threads of Input and Output
* @author liq
*
*/
public class WaitAndNotify {
public static void main(String[] args) {
Storage storage = new Storage();
Input input = new Input(storage);
Output output = new Output(storage);
new Thread(input,"input線程").start();
new Thread(output,"output線程").start();
}
}
//this is Input thread which is used to play a producer role to put a number in storage per second
class Input implements Runnable{
Storage storage;
int num;
public Input(Storage storage) {
this.storage = storage;
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
storage.put(num++);
System.out.println("producer puts num:"+num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//this is Output thread which is used to play a consumer role to get a number in storage per second
class Output implements Runnable{
Storage storage;
public Output(Storage storage) {
this.storage = storage;
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
System.out.println("------------------------consumer gets num:"+storage.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//this is a mutual storage of consumer and producer thread to storage number
class Storage{
private int[] cells = new int[10];
int size = 0; //實際存儲容量
int inPos,outPos;
//往cells中放入一個數
public synchronized void put(int in) {
try {
if (size == 10) { //若存儲已滿,當前線程等待
this.wait();
}
cells[inPos++] = in;
size++;
//超過10位置重新從0開始存放
if (inPos == 10) {
inPos=0;
}
this.notifyAll(); //通知在此同步鎖上等待的其他線程(消費者)開始執行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//從cells中取出一個數
public synchronized int get() {
int result = 0;
try {
//Storage 爲空了,線程等待
if (size == 0) {
this.wait(); //若存儲已空,當前線程等待。
}
result = cells[outPos++];
size--;
//超過大小,從0開始取出
if (outPos == 10) {
outPos = 0;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
}