課程概要:
進(jìn)程與線程
- 線程中單獨(dú)順序的控制流
線程本身依靠程序進(jìn)行運(yùn)行
- 進(jìn)程:執(zhí)行中的程序
一個(gè)進(jìn)程可以包含一個(gè)或者多個(gè)線程
一個(gè)進(jìn)程至少要包含一個(gè)線程 - 單線程
程序中只存在一個(gè)線程,實(shí)際主方法就是一個(gè)主線程 - 多線程:
多線程是在一個(gè)程序中運(yùn)行多個(gè)任務(wù)
多線程的目的是更好地使用CPU資源
線程的實(shí)現(xiàn)

第一種方式:通過繼承thread方式實(shí)現(xiàn)進(jìn)程(需要調(diào)用thread.start()方法才能實(shí)現(xiàn)線程的并發(fā)
public class MyThread extends Thread{
private String name;//用來標(biāo)示線程
public MyThread(String name) {
this.name=name;
}
@Override
public void run() {//必須重寫的方法
for(int i=1;i<=1000;i++)
{
System.out.print(name+i);
if(i%10==0){
System.out.println();
}
}
super.run();
}
}
MyThread t1=new MyThread("A");
MyThread t2=new MyThread("b");
t1.start();
t2.start();
}
第二種方式:
通過實(shí)現(xiàn)runnable 方法,然后獲得runnable對(duì)象,將runnable對(duì)象傳遞給Thread 的構(gòu)造方法,創(chuàng)建thread對(duì)象。然后調(diào)用thread.start()
即可實(shí)現(xiàn)。
示例代碼:
public class MyRunnable implements Runnable{
private String name;
public MyRunnable(String name) {
this.name=name;
}
@Override
public void run() {
for(int i=1;i<=1000;i++)
{
System.out.print(name+i);
if(i%10==0){
System.out.println();
}
}
}
}
MyRunnable r1=new MyRunnable("a");
MyRunnable r2=new MyRunnable("b");
Thread t1=new Thread(r1);
Thread t2=new Thread(r2);
t1.start();
t2.start();
}
輸出結(jié)果:(并發(fā)機(jī)制誰搶到CPU資源就誰執(zhí)行)

線程的狀態(tài)

線程的常用方法
- 取得線程名稱
getName()
- 取得當(dāng)前線程對(duì)象
currentThread() - 判斷線程是否啟動(dòng)
isAlive() - 線程的強(qiáng)行運(yùn)行
join() - 線程的休眠
sleep()
將使任務(wù)終止執(zhí)行給定的時(shí)間。 - 線程的讓步
yield()
這個(gè)方法按時(shí)這個(gè)線程的工作已經(jīng)完成得差不多了,可以讓別的線程使用CPU了,同事也是在建議其他具有相同優(yōu)先級(jí)的線程可以運(yùn)行。
示例代碼(1 2 3):
class MyRunnable2 implements Runnable{
private String name;
public MyRunnable2(String name) {
this.name=name;
}
@Override
public void run() {
for(int i=0;i<5;i++)
{
System.out.println("線程正在運(yùn)行~~~");
}
}
}
public class ThreadDemo02 {
public static void main(String[] args) {
MyRunnable2 r1=new MyRunnable2("jun");
Thread t1=new Thread(r1);
t1.start();
System.out.println("線程是否啟動(dòng):"+t1.isAlive());
System.out.println("當(dāng)前線程對(duì)象是:"+Thread.currentThread());
System.out.println("取得線程名稱:"+t1.getName());
}
}
輸出結(jié)果:

4.join
在其他線程里使用join,能強(qiáng)行讓方法對(duì)象線程執(zhí)行。
例如在主線程里邊強(qiáng)行執(zhí)行自定義線程
示例代碼:
public class ThreadDemo02 {
public static void main(String[] args) {
MyRunnable2 r1=new MyRunnable2("jun");
Thread t1=new Thread(r1);
t1.start();
for(int i=0;i<=50;i++){
if(i>10){
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("主線程"+i);
}
}
}
運(yùn)行結(jié)果:

sleep:
示例代碼:
在run方法里邊調(diào)用
public void run() {
for(int i=0;i<5;i++)
{
try {
Thread.sleep(1000);
System.out.println("線程正在運(yùn)行~~~");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
結(jié)果:控制臺(tái)每隔一秒輸出
線程正在運(yùn)行~~~
線程的優(yōu)先級(jí)
常用方法:
getPriority()讀取現(xiàn)有線程的優(yōu)先級(jí)
setPriority()設(shè)置或者修改線程的優(yōu)先級(jí)
-
注意:
1.java調(diào)度器傾向于讓優(yōu)先級(jí)別高的線程先執(zhí)行(不保證),這并不意味著優(yōu)先級(jí)別比較低的線程就不執(zhí)行(優(yōu)先級(jí)別并不會(huì)導(dǎo)致死鎖)。優(yōu)先級(jí)別低的線程僅僅是執(zhí)行頻率比較低。
2.jdk有10個(gè)優(yōu)先級(jí),但與多數(shù)操作系統(tǒng)都不能映射得很好(比如Windows有七個(gè)優(yōu)先級(jí))。推薦可移植的方法是調(diào)整優(yōu)先級(jí)的時(shí)候只是用MAX_PRIORITY 、 NORM_PRIORITY 、 MIN_PRIORITY
示例代碼:
public class ThreadDemo02 {
public static void main(String[] args) {
MyRunnable1 r1=new MyRunnable1("min");
MyRunnable1 r2=new MyRunnable1("normal");
MyRunnable1 r3=new MyRunnable1("max");
Thread t1=new Thread(r1);
Thread t2=new Thread(r2);
Thread t3=new Thread(r3);
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.NORM_PRIORITY);
t3.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
t3.start();
}
}
輸出結(jié)果:
一開始按順序執(zhí)行,后來優(yōu)先級(jí)別高的線程先執(zhí)行。

同步與死鎖

多線程線程執(zhí)行一個(gè)runnable對(duì)象,由于線程無法共享,會(huì)產(chǎn)生無法預(yù)計(jì)的錯(cuò)誤。
一般在資源需要共享的時(shí)候需要同步。
示例代碼:
創(chuàng)建三個(gè)線程完成一個(gè)run方法,當(dāng)ticket數(shù)為0的時(shí)候停止,預(yù)計(jì)結(jié)果為5、4、3、2、1
class MyRunnable1 implements Runnable{
private String name;
private int tickets=5;
public MyRunnable1(String name) {
this.name=name;
}
@Override
public void run() {
for(int i=0;i<5;i++)
{
if(tickets>0){
try {
Thread.sleep(500);
System.out.println(Thread.currentThread()+"車票"+tickets--);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public class ThreadDemo02 {
public static void main(String[] args) {
MyRunnable1 r1=new MyRunnable1("me");
Thread t1=new Thread(r1);
Thread t2=new Thread(r1);
Thread t3=new Thread(r1);
t1.start();
System.out.println(t1.getName());
t2.start();
System.out.println(t2.getName());
t3.start();
System.out.println(t3.getName());
}
}
輸出結(jié)果:

為了解決這個(gè)問題實(shí)現(xiàn)線程同步,可以使用synchronized關(guān)鍵字
synchronized關(guān)鍵字
它的修飾對(duì)象有幾種:
- 修飾一個(gè)類,其作用的范圍是synchronized后面括號(hào)括起來的部分,作用的對(duì)象是這個(gè)類的所有對(duì)象。
- 修飾一個(gè)方法,被修飾的方法稱為同步方法,其作用的范圍是整個(gè)方法,作用的對(duì)象是調(diào)用這個(gè)方法的對(duì)象;
- 修改一個(gè)靜態(tài)的方法,其作用的范圍是整個(gè)靜態(tài)方法,作用的對(duì)象是這個(gè)類的所有對(duì)象;
- 修飾一個(gè)代碼塊,被修飾的代碼塊稱為同步語句塊,其作用的范圍是大括號(hào){}括起來的代碼,作用的對(duì)象是調(diào)用這個(gè)代碼塊的對(duì)象;
修飾一個(gè)類
其作用的范圍是synchronized后面括號(hào)括起來的部分,作用的對(duì)象是這個(gè)類的所有對(duì)象,如下代碼:
public void method() {
synchronized(ClassName.class) {
// todo
}
}
}
修飾一個(gè)方法
Synchronized修飾一個(gè)方法很簡(jiǎn)單,就是在方法的前面加synchronized,例如:
public synchronized void method(){ // todo}
另外,有幾點(diǎn)需要注意:
- 在定義接口方法時(shí)不能使用synchronized關(guān)鍵字。
- 構(gòu)造方法不能使用synchronized關(guān)鍵字,但可以使用synchronized代碼塊來進(jìn)行同步。
-
synchronized 關(guān)鍵字不能被繼承。如果子類覆蓋了父類的 被 synchronized 關(guān)鍵字修飾的方法,那么子類的該方法只要沒有 synchronized 關(guān)鍵字,那么就默認(rèn)沒有同步,也就是說,不能繼承父類的 synchronized。
修飾靜態(tài)方法
我們知道靜態(tài)方法是屬于類的而不屬于對(duì)象的。同樣的,synchronized修飾的靜態(tài)方法鎖定的是這個(gè)類的所有對(duì)象。如下:
public synchronized static void method() { // todo}
修飾代碼塊
//todo
}```
* 當(dāng)兩個(gè)并發(fā)線程訪問同一個(gè)對(duì)象object中的這個(gè)synchronized(this)同步代碼塊時(shí),一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊。
* 當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí),另一個(gè)線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
* 尤其關(guān)鍵的是,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí),其他線程對(duì)object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
第三個(gè)例子同樣適用其它同步代碼塊。也就是說,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí),它就獲得了這個(gè)object的對(duì)象鎖。結(jié)果,其它線程對(duì)該object對(duì)象所有同步代碼部分的訪問都被暫時(shí)阻塞。
以上規(guī)則對(duì)其它對(duì)象鎖同樣適用.
##線程的生命周期

