线程基础----转

创建

在Java中要實現執行緒功能,可以實作Runnable介面,Runnable介面中只定義一個run()方法,然後實例化一個 Thread物件時,傳入一個實作Runnable介面的物件作為引數,Thread物件會調用Runnable物件的run()方法,進而執行當中所定義的流程
调用方法:
1
x implement Runable
Runable x = new x();
Thread t = new Thread(x);

2匿名方式
Thread thread1 = new Thread(new Runnable() {
public void run() {
});
Thread類別也實作了Runnable介面,您也可以繼承Thread類別並重新定義它的run()方法,好處是可以使用Thread上的一些繼承下來的方法,例如yield(),然而繼承了Thread就表示您不能讓您的類別再繼承其它的類別。

public class MyThread extends Thread {
public run() {
System.out.println(”MyThread.run()”);
}
}

Daemon Thread

主執行緒結束之後,Daemon也就跟著結束.

使用setDaemon()方法來設定一個執行緒是否為Daemon執行緒,使用isDaemon()方法則可以判斷該執行緒是否為Daemon執行緒。

线程生命周期

线程生命周期(转自台湾)

雖然執行緒看起來像是同時執行,但事實上同一時間點上,還是只有一個執行緒在動作,只是執行緒之間切換的動作很快,所以看來像是同時執行。執行緒有其優先權,由1(Thread.MIN_PRIORITY)到10(Thread.MAX_PRIORITY),預設是 Thread.NORM_PRIORITY(5)。
優先權高的執行緒會先被執行完畢,然後才會輪到優先權低的執行緒,如果優先權相同,則輸流執行(Round-robin方式)。
決大多數的作業系統都支援timeslicing,簡單的說就是作業系統會為每個執行緒分配一小段CPU時間(quantum),時間一到就換下一個執行緒,即使現有的執行緒還沒結束。對於不支援timeslicing的作業系統,每一個執行緒必須完成後,才能輪到下一個執行緒,在這樣的作業系統中,如果您想要讓目前執行緒禮讓一下其它執行緒,讓它們有機會取得執行權,您可以在呼叫緒行緒的yield()方法,例如:

public class SomeClass {
// …..
Thread thread = new Thread(new Runnable() {
public void run() {
// ….
while(true) {
// ….
Thread.yield(); // 暫時讓出執行權
}
}
});
thread.start();
// ….
}

yield()方法讓同樣優先權的執行緒有被執行的機會,當執行緒執行yield()方法讓出執行權時,它會再度加入執行緒的排班,等待再度取得執行權,對於支援timeslicing的作業系統,呼叫yield()是不太需要的,因為作業系統會自動分配時間給執行緒輪流執行。

有幾種狀況會讓執行緒進入Not Runnable狀態(或是blocked狀態):

呼叫sleep()
呼叫wait()
等待I/O完成

當執行緒在Not Runnable狀態時,執行緒是可以被執行的,但有某些原因阻止它執行(例如等待使用者的輸入),執行緒排班器將不分配執行時間給這個執行緒,直到以下的幾個情況讓執行緒回到Runnable狀態:

執行緒呼叫notify()
執行緒呼叫notifyAll()
執行緒呼叫interrupt()

當執行緒因為I/O而進入blocked狀態,它必須等到I/O完成才可以離開這個狀態。

最後,如果執行的工作完成(或發生例外)而離開run()方法,則執行緒執行完畢,進入Dead狀態,您可以使用isAlive()方法來測試執行緒是否存活。

如果您查詢Java的線上API文件,您會發現有suspend()、resume()、stop()等方法,這些方法Java並不建議您使用,而且已經被標示為”deprecated”,這些方法建議您在需要的時候自行實作。
當您使用Thread.sleep()讓執行緒暫停執行進入Not Runnable狀態,您可以使用interrupt()讓它離開Not Runnable狀態,當使用sleep()暫時進入Not Runnable狀態而您interrupt()時,會丟出InterruptedException例外物件.

sleep和wait的区别: sleep()不释放同步锁,wait()释放同步锁

sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非
(a)“醒来”的线程具有更高的优先级。
  (b)正在运行的线程因为其它原因而阻塞。

 wait()是线程交互时,如果线程对一个同步对象x 发出一个wait()调用,该线程会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到。
当调用wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用。
waite()和notify()因为会对对象的“锁标志”进行操作,所以它们必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。

join 函数
如果有一個A執行緒正在運行,您希望插入一個B執行緒,並要求B執行緒先執行完畢,然後再繼續A執行緒的流程,您可以使用 join()方法來完成這個需求,這就好比您手頭上正有一個工作在進行,老闆插入一個工作要求您先作好,然後再進行您原先正進行的工作。

线程组

在Java中每個執行緒都屬於某個「執行緒群組」(ThreadGroup)管理的一員,例如若您是在main ()主工作流程中產生一個執行緒,則產生的執行緒屬於main這個執行緒群組管理的一員,您可以使用下面的指令來取得目前執行緒所屬的執行緒群組名稱::

Thread.currentThread().getThreadGroup().getName();

每一個執行緒產生時,都會被歸入某個執行緒群組,這視您的執行緒是在哪個群組中產生,如果沒有指定,則歸入產生該子執行緒的執行緒群組中,您也可以自行指定執行緒群組,執行緒一但歸入某個群組,就無法更換群組。
ThreadGroup中的某些方法,可以對所有的執行緒產生作用,例如interrupt()可以interrupt群組中所有的线程.

synchronized
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

“synchronized”的一個使用方式是用於方法上,讓方法作用範圍內都成為被同步化區域,例如:

public synchronized void setNameAndID(String name,
String id) {
this.name = name;
this.id = id;
if(!checkNameAndIDEqual()) {
System.out.println(count +
“) illegal name or ID…..”);
}
count++;
}

每個物件內部都會有一個鎖定(lock),當執行緒執行某個物件的同步化方法時,它會在物件上得到這個鎖定,只有取得鎖定的執行緒才可進入同步區,未取得鎖定的執行緒則必須等待,直到有機會取得鎖定,其它執行緒必須等目前執行緒先執行完同步化方法,並解除對物件的鎖定,才有機會取得物件上的鎖定。

就這個例子來說,簡單的說,就是有執行緒在執行setNameAndID()時,會從物件上取得鎖定,其它執行緒必須等待它執行完畢,釋放鎖定之後,才會有機會競爭鎖定,取得鎖定的執行緒才可以執行setNameAndID ()。

以上所介紹的是實例方法同步化(instance method synchronized),同步化的設定不只可用於方法上,也可以用於某個程式區塊上,稱之為實例區塊同步化(instance block synchronized),例如:

public void setNameAndID(String name, String id) {
synchronized(this) {
this.name = name;
this.id = id;
if(!checkNameAndIDEqual()) {
System.out.println(count +
“) illegal name or ID…..”);
}
count++;
}
}

上面的意思就是在執行緒執行至”synchronized”設定的區塊時取得物件的鎖定,這麼一來其它執行緒暫時無法取得鎖定,因此無法執行物件同步化區塊,這個方式可以應用於您不想鎖定整個方法區塊,而只是想在共享資料在被執行緒存取時確保同步化時,由於只鎖定方法中的某個區塊,在執行完區塊後即釋放對物件的鎖定,以便讓其它執行緒有機會取得鎖定,對物件進行操作,在某些時候會比較有效率。

實例區塊同步化的好處是,您也可以對某個物件進行同步化,而像實例方法同步化只針對this,例如在多執行緒存取同一個ArrayList物件時,ArrayList並沒有實作資料存取時的同步化,所以它使用於多執行緒時,必須注意是否必須對它進行同步化,多個執行緒存取同一個ArrayList時,有可能發生兩個以上的執行緒將資料存入 ArrayList的同一個位置,造成資料的相互覆蓋,為了確保資料存入時的正確性,您可以在存取ArrayList物件時對它進行同步化,例如:

// arraylist參考至一個ArrayList的一個實例
synchronized(arraylist) {
arraylist.add(new SomeClass());
}

除了針對物件同步之外,您還可以針對靜態方法同步化(static method synchronized),例如某個static成員會被多執行緒存取時,則可以如下設定:
public class Some {
private static int value;

public synchronized static void some() {
value++;
….
}
}

進行鎖定時,會鎖定Some.class,因而static成員也受到保護。類似於實例區塊同步化,您也可以在區塊中鎖定整個類別,稱之為類別字面同步化(class literals synchronized),例如:

public void doSomething() {
synchronized(Some.class) {
….
}
}

事實上,您也可以使用Collections的synchronizedXXX()等方法來傳回一個同步化的容器物件,例如傳回一個同步化的List:

List list = Collections.synchronizedList(new ArrayList());

同步化所犧性的自然就是在於執行緒等待時的延遲,所以同步化的手法不應被濫用,您不用將整個物件的方法都加上”synchronized”,有些方法只是單純的傳回某些數值,它並沒有對共用資料進行修改的動作,那麼它就不需要被同步化。

notify and wait
wait()、notify()與notifyAll()是由 Object所提供的方法,您在定義自己的類別時會繼承下來(記得Java中所有的物件最頂層都繼承自Object),wait()、notify()與 notifyAll()都被宣告為”final”,所以您無法重新定義它們,透過這三個方法您可以控制執行緒是否為Runnable狀態。

您必須在同步化的方法或區塊中呼叫wait()方法,當物件的wait()方法被調用,目前的執行緒會被放入物件的「等待集合」(Wait set)中,執行緒會釋放物件的鎖定,其它的執行緒可以競爭鎖定,取得鎖定的執行緒可以執行同步化區塊;被放在等待集中的執行緒將不參與執行緒的排班,wait()可以指定等待的時間,如果指定時間的話,則時間到之後執行緒會再度加入排班,如果指定時間0或不指定,則執行緒會持續等待,直到有被中斷(interrupt)或是被告知(notify)可以參與排班。

當物件的notify()被調用,它會從物件的等待集中選出「一個」執行緒加入排班,被選出的執行緒是隨機的,被選出的執行緒會與其它正在執行的執行緒共同競爭對物件的鎖定;如果您呼叫notifyAll(),則「所有」在等待集中的執行緒都會被喚醒,這些執行緒會與其它正在執行的執行緒共同競爭對物件的鎖定。

簡單的說,當執行緒呼叫到物件的wait()方法時,表示它要先讓出物件的被同步區使用權並等待通知,或是等待一段指定的時間,直到被通知或時間到時再從等待點開始執行,這就好比您要叫某人作事,作到一半時某人叫您等候通知(或等候1分鐘之類的),當您被通知(或時間到時)某人會繼承為您服務。

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值