Java基礎(包、多線程)


一、包

作用:

1、用於給類文件進行分類管理

2、給類提供多層命名空間

特點:

1、必須寫在程序中的第一行

2、類的全稱是包名.類名

3、其實也是一種封裝形式

聲明包:格式    package    包名

自動生成包: 格式:   javac  -d   空格 . 空格  要編譯的Java文件

注:

1、-d是為了指定包存放的位置,-d后面可以跟路徑  “空格 . 空格”代表的是在cd所跳到的目錄下存放

2、聲明包后,如果要運行Java字節碼文件(.class)文件,則必須要使用包名.類名進行訪問  如:聲明的包為page ,類為Test,那么你在dos命令行要運行Test,則必須通過 Java page.Test來訪問

包的好處:可以讓程序中的類文件與源文件分離

不同包中的類如何訪問:

1、A包想要使用B包中的類,必須對B包中的類進行標識  也就是說在創建B包中的類時,需要帶上包名

2、當A包與B包不在同一目錄下時,,需要告訴jvm去哪里找指定的包  需要配置classpath

3、包與包之間進行訪問,如果不存在關系(繼承、接口),那么被訪問的包中的類以及類中的成員必須被public修飾

4、如果包與包之間之間存在關系,那么可以直接訪問被public和protected修飾符修飾的成員

四中權限:

publicprotected默認private
同一個類中 OK OK OK OK
同一個包中 OK OK OK NO
不同包中子類 OK OK NO NO
不同包中 OK NO NO NO

權限修飾符大小:public > protected > 默認 > private

導入包:通過inport關鍵字實現   import  包名

包名定義規范:

1、可以使用URL來完成,因為URL是唯一的          格式為:  如  www.baidu.com              寫成包名就是:com.baidu.類名

2、包名當有多個單詞組成時,所有字母均小寫

Jar包:DOS命令行中輸入jar可以查看具體參數

把多個包壓縮成jar包:格式   jar -cf  生成的jar包名  要壓縮的文件夾(包名)

查看jar包中的文件信息:格式: jar -tf 要查看的架文件名

把jar包中的信息導入到文件中(數據重定向): 格式: jar -tf  要導出的Jar文件 > 要導入的路徑

注意:

1、有一個小細節特別致命  在jdk所在的路徑下的lib文件夾下有一個可執行文件  tools.jar  他是用來保證javac正常執行的,不可以更換路徑會該名稱

2、Jdk所在目錄下有一個壓縮文件  src.zip  他是Java的所有類的源文件,可以修改

二、多線程

一、什么是多線程?

要想了解線程就必須要先了解進程,什么是進程呢?大家都玩過任務管理器吧?嗯哼,是不是懂了一點??

1、進程:其實進程就是一個正在執行中的程序,每一個進程執行都有一個執行順序,該順序是一個執行路徑(或者叫一個控制單元)

2、線程:其實就是進程中的一個獨立的控制單元,線程在控制着進程的執行

注:一個進程中至少有一個線程

3、多線程

       java虛擬機啟動的時候會有一個java.exe的執行程序,也就是一個進程。該進程中至少有一個線程負責java程序的執行。而且這個線程運行的代碼存在於main方法中。該線程稱之為主線程。JVM啟動除了執行一個主線程,還有負責垃圾回收機制的線程。像種在一個進程中有多個線程執行的方式,就叫做多線程。

4、多線程存在的意義

        多線程的出現能讓程序產生同時運行效果。可以提高程序執行效率。

        例如:在java.exe進程執行主線程時,如果程序代碼特別多,在堆內存中產生了很多對象,而同時對象調用完后,就成了垃圾。如果垃圾過多就有可能是堆內存出現內存不足的現象,只是如果只有一個線程工作的話,程序的執行將會很低效。而如果有另一個線程幫助處理的話,如垃圾回收機制線程來幫助回收垃圾的話,程序的運行將變得更有效率。

5、計算機CPU的運行原理

        我們電腦上有很多的程序在同時進行,就好像cpu在同時處理這所以程序一樣。但是,在一個時刻,單核的cpu只能運行一個程序。而我們看到的同時運行效果,只是cpu在多個進程間做着快速切換動作。

        cpu執行哪個程序,是毫無規律性的。這也是多線程的一個特性:隨機性。哪個線程被cpu執行,或者說搶到了cpu的執行權,哪個線程就執行。而cpu不會只執行一個,當執行一個一會后,又會去執行另一個,或者說另一個搶走了cpu的執行權。至於究竟是怎么樣執行的,只能由cpu決定。

二、創建線程

創建線程有兩種方式:繼承方式和實現方式

一、繼承方式

通過對API的查找,我們發現Java中已經提供對線程這類食物的描述,就是Thread類,因此我們只需要繼承Thread類就可以了

1、步驟:

1、定義類繼承Thread類

2、重寫Thread類中的run方法

復寫run方法僅僅是為了將自定義的代碼存儲(封裝)在run方法中,並不會開啟線程,還是一個線程,如果僅僅是調用run方法,那么調用一般函數沒什么區別

3、創建子類對象,相當於創建一個線程

4、調用線程中的start方法(用於開啟線程)

該方法有兩個作用:1、啟動線程    2、調用run方法

注:如果直接使用對象調用run方法,僅僅代表的是創建了線程,並沒有運行,而要運行線程,必須調用start方法

2、為什么一定要復寫run方法?

原因:Thread用於描述線程,該類就定義了一個功能用於存儲線程需要運行的代碼,該存儲功能就是fun方法,也就是說Thread類中的run方法就是用來存儲線程要運行的代碼的。

3、代碼實現:

class Demo extends Thread
{
//覆蓋父類中的run方法
 public void run(){
for(int x=0;x<30;x++){
System.out.println("Demo......."+x);
}
}
}

class ThreadDemo1
{
public static void main(String[] args)
{
Demo d = new Demo();//創建一個線程
d.start();//開啟線程並調用run方法

//主線程
for(int i=0;i<30;i++){
System.out.println("main....."+i);
}
}
}</span>

4、多線程小練習

/*
需求:創建兩個線程,和主線程交替運行。並打印線程名稱

如何打印線程名稱呢?
我們可以通過父類中提供的方法進行獲取 getName()或Thread。currentThread().getName()
注意:currentThread是靜態的,所以可以直接使用類名.調用 是返回對當前正在執行的線程對象的引用。 調用時相當於this


//通過運行發現,線程名稱打印出來了 格式是:Thread-編號 編號從0開始
那么我們怎么自定義線程名稱呢?
通過查看API我們發現Thread類中已經定義了Thread(String name)構造函數 所以可以通過子類構造函數向父類傳參
*/
class Demo extends Thread
{
//初始化線程名
Demo(String name){
super(name);
}
public void run(){
System.out.println("Thread.currentThread()==this="+Thread.currentThread()==this);//結果為true
for(int x=0;x<60;x++){
System.out.println(this.getName()+".....run...."+x);
}
}
}


class ThreadDemo2
{
public static void main(String[] args)
{
//System.out.println("Hello World!");
Demo d1 = new Demo("One");
Demo d2 = new Demo("Two");
d1.start();
d2.start();


//主線程
for(int i=0;i<40;i++){
System.out.println(Thread.currentThread().getName()+"......run"+i);
}
}
}


二、實現方式

1、步驟:

1、定義類實現Runnable接口

2、復寫Runnable中的run方法

3、通過Runnable接口的子類創建對象

4、把Runnable接口的子類對象作為實際參數傳遞給Thread類的構造函數

5、調用Thread類的start方法,開啟線程,並調用Runnable接口的run方法

注:為什么一定要將Runnable接口的之類對象作為實際參數傳遞給Thread類的構造函數?

原因:因為自定義的run方法所屬的對象時Runnable接口的子類對象,而該對象中是沒有開啟線程的方法的,所以要讓線程執行指定對象的run方法就必須明確run方法所屬的對象,然后調用start方法開啟線程

2、實現方式的好處:避免了單繼承出現的局限性,在定義線程是建議使用實現方式

注:為什么建議使用實現方式?

原因:因為使用繼承創建線程會有一個弊端,那就是要繼承Thread類,如果自定義類已經繼承了其他類,那么在使用繼承方式時,由於該類已經繼承了其他類,而Java並不支持多繼承,所以就不能再繼承Thread類了,出現了局限性。而實現方式卻不會發生這種情況,因為他可以在繼承其他類的同時在實現Runnable接口

3、示例程序:

/*
需求:用多線程實現多個窗口賣票
*/
//使用繼承方式
class ExtendsThread extends Thread
{
private int tick = 100;//繼承方式 共享數據會出現不正常情況
public void run(){
while(true){//控制程序一直循環,知道票賣完
if(tick>0)
System.out.println(Thread.currentThread().getName()+".....run...."+tick--);
}
}
}

//使用實現方式
class ImplementThread implements Runnable
{
private int tick = 100;
public void run(){
while(true){//控制程序一直循環,知道票賣完
if(tick>0)
System.out.println(Thread.currentThread().getName()+".....run...."+tick--);
}
}
}
class ThreadDemo3
{
public static void main(String[] args)
{
//System.out.println("Hello World!");
//使用繼承方式創建線程
/*
ExtendsThread et1 = new ExtendsThread();//創建一個線程
ExtendsThread et2 = new ExtendsThread();
ExtendsThread et3 = new ExtendsThread();
ExtendsThread et4 = new ExtendsThread();
et1.start(); //開啟線程
et2.start();
et3.start();
et4.start();
*/
/*通過運行發現,使用繼承出現了不正常數據,所以使用繼承創建多線程時,如果有使用到共享數據時,共享數據必須被static修飾,不然就會出現不正常數據 而實現會么???*/


//使用實現方式創建線程
ImplementThread it = new ImplementThread();//創建Runable接口子類對象
Thread t1 = new Thread(it); //創建線程
Thread t2 = new Thread(it);
Thread t3 = new Thread(it);
Thread t4 = new Thread(it);
t1.start(); //開啟線程
t2.start();
t3.start();
t4.start();

/*運行后發現並沒有出現繼承方式中出現的問題,因為使用實現方式操作的是同一個對象的tick,數據共享了*/

}
}
實現方式運行結果:


繼承方式運行結果:


注:通過繼承方式運行結果發現不同線程中出現了重復值得情況,說明線程操作的不是同一個對象

4、實現方式和繼承方式的區別:

繼承Thread:線程代碼存放在Thread子類run方法中。

實現Runnable:線程代碼存放在接口子類run方法中。

繼承操作的不是同一個Thread類的子類對象,而實現操作的是同一個Runnable接口的子類對象

三、線程中的幾種狀態

        被創建:等待啟動,調用start啟動。

        運行狀態:具有執行資格和執行權。

        臨時狀態(阻塞):有執行資格,但是沒有執行權。

        凍結狀態:遇到sleeptime)方法和wait()方法時,失去執行資格和執行權,sleep方法時間到或者調用notify()方法時,獲得執行資格,變為臨時狀態。

        消忙狀態:stop()方法,或者run方法結束。

注:當已經從創建狀態到了運行狀態,再次調用start()方法時,就失去意義了,java運行時會提示線程狀態異常。

圖解:

注:這里的wait()方法和sleet()方法是有區別的:wait()會釋放cpu執行權,釋放鎖。而sleep雖然也會釋放執行權,但是卻不會釋放鎖

四、線程安全問題

導致問題出現的原因:

當多條語句在操作同一個線程共享數據時,一個線程對多條語句還沒有執行完,另一個線程得到了執行權,參與運行了,導致共享數據錯誤

解決辦法:

對多條操作共享數據的語句,在某個時間段,讓一個線程把有共享數據參與的語句一次性執行完,在該線程執行過程中,其他進程不可以參與執行

解決體現形式:同步代碼塊和同步函數

同步原理:對象就如同鎖,持有鎖的線程可以在同步中執行,沒有持有所得線程即使獲取了cpu的執行權,也進不去,因為沒有獲取鎖,也就是沒有鑰匙,比較經典的例子:火車上的衛生間

1、同步代碼塊

用法: synchronized(對象)

{

需要同步的代碼;

}

示例:

/*
需求:給買票程序加上同步代碼塊
*/
class ImplementThread implements Runnable
{
private int tick = 100;
Object obj = new Object();
public void run(){
while(true){//控制程序一直循環,知道票賣完
//同步,也就是上鎖
synchronized(obj){
if(tick>0){
try
{
//使用線程中的sleep方法,模擬線程可能出現的問題
//因為sleep方法有拋出異常,而run方法不能拋出異常,所以應在內部try處理
Thread.sleep(10);
}
catch (Exception e)
{
}
System.out.println(Thread.currentThread().getName()+".....run...."+tick--);
}
}
}
}
}
class ThreadDemo4
{
public static void main(String[] args)
{
//System.out.println("Hello World!");
ImplementThread it = new ImplementThread();//創建Runable接口子類對象
Thread t1 = new Thread(it);
Thread t2 = new Thread(it);
Thread t3 = new Thread(it);
Thread t4 = new Thread(it);
t1.start();//開啟線程
t2.start();
t3.start();
t4.start();
}
}
未同步運行結果:

同步后結果:


2、同步函數

        格式:在函數加上同步即可:public synchronized void show(){}

        那么同步函數用的是哪一個鎖呢?

        函數需要被對象調用。那么函數都有一個所屬對象引用。就是this。所以同步函數使用的鎖是this

示例:

/*
需求:給買票程序加上同步代碼塊
*/
class ImplementThread implements Runnable
{
private int tick = 100;
Object obj = new Object();
public void run(){
while(true){//控制程序一直循環,知道票賣完
show();
}
}
private synchronized void show(){
if(tick>0){
try
{
//使用線程中的sleep方法,模擬線程可能出現的問題
//因為sleep方法有拋出異常,而run方法不能拋出異常,所以應在內部try處理
Thread.sleep(10);
}
catch (Exception e)
{
}
System.out.println(Thread.currentThread().getName()+".....run...."+tick--);
}
}
}
class ThreadDemo5
{
public static void main(String[] args)
{
//System.out.println("Hello World!");
ImplementThread it = new ImplementThread();//創建Runable接口子類對象
Thread t1 = new Thread(it);
Thread t2 = new Thread(it);
Thread t3 = new Thread(it);
Thread t4 = new Thread(it);
t1.start();//開啟線程
t2.start();
t3.start();
t4.start();
}
}

3、同步的前提

1、必須要有兩個或兩個以上的線程

2、必須是多個線程使用同一個鎖

4、同步利弊

1、解決了多線程的安全問題

2、多線程都需要判斷鎖,較為消耗資源,降低了程序效率

5、如何找線程中的問題

1、明確哪些代碼是多線程要運行的代碼

2、明確共享數據

3、明確多線程運行代碼中那些語句是操作共享數據的

6、靜態同步函數

靜態同步函數使用的鎖匙什么呢?

原理:通過驗證發現不在是this,因為靜態方法中不可以定義this,靜態進內存是,內存中沒有本類對象,但是一定有該類對應的字節碼對象,類名.class該對象的類型是class,所以靜態同步函數使用的鎖匙該方法所在類的字節碼 文件對象,也就是類名.class,並且該對象在內存中是唯一的,因為字節碼文件是唯一的

示例:

/*靜態同步函數*/

class ImplementThread implements Runnable
{
private static int tick = 100;
Object obj = new Object();
public void run(){
while(true){//控制程序一直循環,知道票賣完
//show();
//靜態同步代碼塊
/**/
//synchronized使用的鎖是所在類的字節碼文件 類名.class
synchronized(ImplementThread.class){
if(tick>0){
try
{
//使用線程中的sleep方法,模擬線程可能出現的問題
//因為sleep方法有拋出異常,而run方法不能拋出異常,所以應在內部try處理
Thread.sleep(10);
}
catch (Exception e)
{
}
System.out.println(Thread.currentThread().getName()+".....run...."+tick--);
}
}

}
}
//靜態同步函數
private static synchronized void show(){
if(tick>0){
try
{
//使用線程中的sleep方法,模擬線程可能出現的問題
//因為sleep方法有拋出異常,而run方法不能拋出異常,所以應在內部try處理
Thread.sleep(10);
}
catch (Exception e)
{
}
System.out.println(Thread.currentThread().getName()+".....run...."+tick--);
}
}
}
class ThreadDemo7
{
public static void main(String[] args)
{
//System.out.println("Hello World!");
ImplementThread it = new ImplementThread();//創建Runable接口子類對象
Thread t1 = new Thread(it);
Thread t2 = new Thread(it);
Thread t3 = new Thread(it);
Thread t4 = new Thread(it);
t1.start();//開啟線程
t2.start();
t3.start();
t4.start();
}
}

五、死鎖

可能出現死鎖的情況:通常都是同步中嵌套同步而鎖卻不同時

寫一個死鎖程序:

/*
需求:寫一個死鎖程序
原理:同步里面嵌套同步,
*/
//自定義兩個鎖
class Demo
{
public static Object demoA = new Object(); //創建A鎖
public static Object demoB = new Object(); //創建B鎖
}
class DemoTest implements Runnable
{
int x = 0;
private boolean falg = true;
DemoTest(boolean falg){
this.falg = falg;
}
public void run(){
if(falg){
while(true){
synchronized(Demo.demoA){ //使用A鎖
System.out.println(Thread.currentThread().getName()+"......demoA....");
synchronized(Demo.demoB){ //使用B鎖
System.out.println(Thread.currentThread().getName()+"......demoB....");
}
}
}

}
else{
while(true){
synchronized(Demo.demoB){ //使用B鎖
System.out.println(Thread.currentThread().getName()+"......demoB....");
synchronized(Demo.demoA){ //使用A鎖
System.out.println(Thread.currentThread().getName()+"......demoA....");
}
}
}
}
}
}
class ThreadDemo8
{
public static void main(String [] agrs){
new Thread(new DemoTest(true)).start(); //啟動線程
new Thread(new DemoTest(false)).start();
}
}

結果:程序被卡住,不能執行


六、線程間通訊

定義:其實就是多個線程在操作同一個資源,但是操作的動作不同

示例圖:



代碼體現一:

/*需求:使用多線程實現線程間通信,要求一個線程往資源中添加,另一個線程往外輸出添加的內容
思路:1、要使用線程通信,必須要有資源類
2、根據需求:需要兩個線程同時操作一個資源,而要實現同步,必須保證這兩個線程都在操作同一把鎖
*/

//定義資源類
class Res
{
//定義成員變量用於作為共享數據被線程進行添加和輸出
private String name;
private String sex;
//用於讓線程等待
private boolean flag = false;
//定義輸入線程要操作的代碼,由於數據是成對(name,set)添加的,所以應同步
public synchronized void setInput(String name, String sex){
if(flag)
try{
System.out.println(Thread.currentThread().getName()+"+....................................................11111wait");
wait();
}
catch(Exception e){

}
//這里只能使用if語句,不可以使用if else語句,因為無論flag是否為true,下面代碼都是要執行的,flag只是用來防止覆蓋用的
this.name = name;
this.sex = sex;
flag = true;
System.out.println("..........................................1111執行了..."+flag+"..."+name+"...."+sex);
notify();
}
//定義輸出線程要操作的代碼,因為要使用同步,所以必須保證至少有兩把鎖,所以該函數也需要同步
public synchronized void getOutput(){
if(!flag)
try{
System.out.println(Thread.currentThread().getName()+"+....................................................2222wait");
wait();
}
catch(Exception e){

}
System.out.println(Thread.currentThread().getName()+"..."+name+"...."+sex);
flag = false;
System.out.println("..........................................2222執行了...flat="+flag);
notify();
}

}
//定義輸入線程
class Input implements Runnable
{
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
if(x%2==0)
r.setInput("MK","Man");
else
r.setInput("張三","男男男男");
//System.out.println("........................................x="+x);
x++;
}
}
}
//定義輸出(打印)線程
class Output implements Runnable
{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
r.getOutput();
}
}
}

class ThreadDemo9
{
public static void main(String[] args)
{
Res r = new Res();//把對象作為實際參數傳遞,保證線程操作的資源是同一個
new Thread(new Input(r)).start();//啟動線程
new Thread(new Output(r)).start();
}
}

練習2

/*
需求:要求使用線程通信完成多個輸入線程和多個輸出線程同時操作資源
思路:和剛剛我們做的差不多,稍微改下代碼就行
*/
//定義資源類
class Res
{
//定義成員變量用於作為共享數據被線程進行添加和輸出
private String name;
private String sex;
//用於讓線程等待
private boolean flag = false;
//定義輸入線程要操作的代碼,由於數據是成對(name,set)添加的,所以應同步
public synchronized void setInput(String name, String sex){
//這里需要改變,因為是多個線程在操作同一段代碼,喚醒后都需要判斷
while(flag)
try{
System.out.println("............................................."+Thread.currentThread().getName()+"wait++++++++"+flag);
wait();//t1(放棄)(獲取) t2(放棄)(獲取)
}
catch(Exception e){

}
//這里只能使用if語句,不可以使用if else語句,因為無論flag是否為true,下面代碼都是要執行的,flag只是用來防止覆蓋用的
this.name = name;
this.sex = sex;
System.out.println(Thread.currentThread().getName()+"...."+name+"...."+sex+".........................新添");
flag = true;
notifyAll();//喚醒線程池中的所有線程,防止線程全部等待
}
//定義輸出線程要操作的代碼,因為要使用同步,所以必須保證至少有兩把鎖,所以該函數也需要同步
public synchronized void getOutput(){
while(!flag)
try{
System.out.println("................................................."+Thread.currentThread().getName()+"wait——————"+flag);
wait();//t3(放棄)(獲取) t4(放棄)(獲取)
}
catch(Exception e){

}
System.out.println(Thread.currentThread().getName()+"........."+name+"...."+sex+"++++++++++++++++打印");
flag = false;
notifyAll();
}

}
//定義輸入線程
class Input implements Runnable
{
private int x;
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
while(true){
if(x%2==0)
r.setInput("MK","Man");
else
r.setInput("張三","男男男男");
x++;
}
}
}
//定義輸出(打印)線程
class Output implements Runnable
{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
r.getOutput();
}
}
}
class ThreadDemo10
{
public static void main(String [] args){
Res r = new Res();
//創建輸入對象,並作為實際參數傳遞,保證線程使用的是同一個對象
Input in = new Input(r);
Output out = new Output(r);
new Thread(in).start();
new Thread(in).start();
new Thread(out).start();
new Thread(out).start();
}
}


七、wait()、notify()、notifyAll()

作用:都是使用在同步中,因為要對持有監視器(鎖)的線程操作

為什么這些操作線程的方法一定要定義在Object類中呢?

原因:因為這些方法在操作同步中的線程時,都必須標識他們所操作線程持有的鎖,只有同一個鎖上的被等待的線程可以被同一個鎖上的notify喚醒,不可以對不同鎖中的線程進行喚醒,以為不是同一把鎖,也就是說:等待和喚醒必須是同一把鎖,而鎖可以是任意對象,所以可以被任意對象調用的方法要定義在Object類中

八、JDK1.5版本中提供的多線程升級方案

1.5版本新特性:

1、將同步synchronized替換成了顯式Lock操作

2、將Object類中的wait、notify、notifyAll替換成了condition對象,該對象可以對lock鎖進行獲取

3、可以實現只喚醒對方的操作

結論:1.5版本之后提供了顯式的鎖機制,以及顯式的鎖對象上的等待喚醒操作機制,同時,它把等待喚醒機制進行了封裝,封裝完一個鎖可以對應多個等待和喚醒

使用多線程升級方案演示線程間通信:

/*
需求:使用JDK1.5版本提供的多線程解決方法優化該項目
*/
//定義資源類
import java.util.concurrent.locks.*;
class Res
{
//定義成員變量用於作為共享數據被線程進行添加和輸出
private String name;
private String sex;
//用於讓線程等待
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
//定義輸入線程要操作的代碼,由於數據是成對(name,set)添加的,所以應同步
//public synchronized void setInput(String name, String sex){
public void setInput(String name,String sex)throws Exception//condition_pro.wait();會有異常,在這里我們拋出
{
//因為需要同步,所以應使用同步,1.5版本后使用Lock類中提供的lock方法開啟鎖
lock.lock();
//這里需要改變,因為是多個線程在操作同一段代碼,喚醒后都需要判斷
try{
while(flag)
//wait();//t1(放棄)(獲取) t2(放棄)(獲取)
//等待被替換為
condition_pro.wait();
this.name = name;
this.sex = sex;
System.out.println(Thread.currentThread().getName()+"...."+name+"...."+sex+".........................新添");
flag = true;
//notifyAll();//喚醒線程池中的所有線程,防止線程全部等待
//notifyAll()喚醒被替換為 可以只用來喚醒對方線程
condition_con.signal();
}
finally{
//關閉鎖 必須放在finally語句快中
lock.unlock();
}
}
//定義輸出線程要操作的代碼,因為要使用同步,所以必須保證至少有兩把鎖,所以該函數也需要同步
//public synchronized void getOutput(){
//可以不用再函數上上鎖
public void getOutput()throws Exception//condition_con.wait();會有異常,在這里我們拋出
{
lock.lock();
try{
while(!flag)
//wait();//t3(放棄)(獲取) t4(放棄)(獲取)
//wait()被替換為:
condition_con.wait();
System.out.println(Thread.currentThread().getName()+"........."+name+"...."+sex+"++++++++++++++++打印");
flag = false;
//notifyAll();
//notifyAll被替換為:
condition_pro.signal();
}
finally{
lock.unlock();
}

}

}
//定義輸入線程
class Input implements Runnable
{
private int x;
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
while(true){
try
{
r.setInput("MK","Man");
}
catch (Exception e)
{
}
}
}
}
//定義輸出(打印)線程
class Output implements Runnable
{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
try
{
r.getOutput();
}
catch (Exception e)
{
}
}
}
}
class ThreadDemo11
{
public static void main(String [] args){
Res r = new Res();
//創建輸入對象,並作為實際參數傳遞,保證線程使用的是同一個對象
Input in = new Input(r);
Output out = new Output(r);
new Thread(in).start();
new Thread(in).start();
new Thread(out).start();
new Thread(out).start();
}
}

九、停止線程

停止線程的方式:

1、開啟多線程運行,運行代碼通常是循環結構。只要控制住循環,就可以讓run方法結束,也就是線程結束。

2、當遇到特殊情況時,比如線程處於了凍結狀態,則第一種方式是沒有辦法結束線程的,當沒有指定的方式讓凍結的線程恢復到運行狀態時,這時需要對凍結進行清除。強制讓線程恢復到運行狀態中來。這樣就可以操作標記讓線程結束。實現方式:Thread類提供的interrupt();方法 

class StopThread implements Runnable
{
private boolean flag =true;
public void run()
{
while(flag)
{

System.out.println(Thread.currentThread().getName()+"....run");
}
}
public void changeFlag()
{
flag = false;
}
}




class ThreadDemo12
{
public static void main(String[] args)
{
StopThread st = new StopThread();

Thread t1 = new Thread(st);
Thread t2 = new Thread(st);


t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();

int num = 0;
//用主線程控制t1、t2線程結束
while(true)
{
//當滿足該條件時,讓t1、t2線程結束
if(num++ == 60)
{
st.changeFlag();//利用控制線程循環條件,結束循環
t1.interrupt();//如果線程處於凍結狀態,強制讓線程回到運行狀態,因為在凍結狀態下無法操作標記,沒辦法操作標記,那么線程就停不下來
t2.interrupt();
System.out.println(Thread.currentThread().getName()+"......."+num);
break;//用於結束主線程
}
System.out.println("......."+num);
try{Thread.sleep(10);}catch(Exception e){}
}
}
}
十、線程類的其他方法

1、setDaemon(boolean boo)           返回值類型為 void

主要將該線程標記為守護線程或用戶線程。

理解:守護線程可以理解為后台線程,而我們看到的線程其實都是前台線程,當你把某些線程表姐為后台線程后,他就具備了一個特殊的含義,后台線程的特點:開啟后會和前台線程共同搶奪cpu的執行權運行,開啟運行都沒有區別,就結束有區別,當uoyou的前台線程都結束后,后台線程會自動結束,后台線程依賴於前台線程

2、join()          返回值類型void

主要用於等待線程終止 

理解:當主線程執行到join方法時,凡是在該方法后面的線程都會被處於凍結狀態,只用當join線程結束后,后面的線程才會被執行

3、setPriority(int newPriority)   返回值類型為void

主要用於修改線程的優先級

線程優先級等級:     1級——10級   默認為5級

各個等級在Java中已經定義好了最終靜態常量保存了1級、5級和10級   如圖:

注:在給線程設置優先級時,只需要使用Thread類調用即可  如: setPriority(Thread.MAX_PKIORITY)    //設置最高優先級

4、yieId()    static  void

用於暫停當前正在執行的線程對象,並執行其他線程,就好比說我執行完了,該你了(就是你執行完聽一次當搶奪到執行權后在執行,在等待...................)

線程終於寫完了!!  完美!!!









































注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



Java基礎(九)——多線程 Java多線程基礎 Java多線程基礎 Java基礎:多線程 java基礎-多線程 Java多線程基礎 Java多線程基礎 Java多線程-基礎 java多線程基礎 java 多線程基礎
 
粤ICP备14056181号  © 2014-2020 ITdaan.com