java多線程學習1

一:多線程簡介

進程:每個進程(程序)都有獨立的代碼和數據空間(進程上下文)。進程間的切換會有較大的開銷,一個進程包括1--n個線程。(進程是資源分配的最小單位)java

線程:同一類線程共享代碼和數據空間,每個線程有獨立的執行棧和程序計數器(PC),線程切換開銷小。(線程是cpu調度的最小單位)網絡

多進程:是指操做系統能同一時候執行多個任務(程序)。多線程

多線程:是指在同一程序中有多個順序流在執行。併發

java中要想實現多線程,主要有兩種方式,繼承Thread類或者實現Runable接口,至於實現Callable接口方式,不經常使用ide

二:實現多線程

  (1)方式1:繼承Thread類

  不推薦繼承Thread類,由於java是單繼承,能實現接口使用的不推薦使用繼承,而且Thread類也是實現了Runable接口,而且提供了run()方法用於啓動線程工具

package com.bjsxt.thread;

/**
 * Created by Administrator on 2019/3/9.
 */
public class TestThread01 extends Thread{

    /**
     * 線程的入口點
     */
    @Override
    public void run() {
        for (int i=0;i<100;i++){
            System.out.println("一遍聽歌。。。。");
        }
    }

    public static void main(String[] args){
        TestThread01 testThread01=new TestThread01();   //建立對象
        //啓動線程,不保證會當即執行,只是建立了個線程,把執行權交給cpu,至於何時執行要看cpu調度
        //若是寫成testThread01.run()就是普通方法的調用了
        testThread01.start();

        for(int i=0;i<100;i++){
            System.out.println("一遍敲代碼。。。");
        }
    }
}

執行結果測試

 

案例:同時開啓三個線程下載一個網絡url圖片ui

  一、先寫一個文件下載的工具類FileUtilsthis

  

package com.bjsxt.thread;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * Created by Administrator on 2019/3/9.
 */
public class FileUtils {

    /**
     *
     * @param url   文件遠程url路徑
     * @param name  文件保存位置
     */
    public void copyURLFile(String url,String name){
        try(
            //建立文件輸入緩衝流,讀取文件信息
            BufferedInputStream bis=new BufferedInputStream(new URL(url).openStream());
            //建立文件輸出流,寫出文件信息
            BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(name));
         ) {

            byte[] bytes=new byte[1024];
            int len=-1;
            while((len=bis.read(bytes))!=-1){
                bos.write(bytes,0,len);
                bos.flush();
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}

  建立線程類,開啓下載url

package com.bjsxt.thread;

/**
 * Created by Administrator on 2019/3/9.
 */
public class TestThread02 extends Thread {

    private String name;
    private String path;

    public TestThread02(String name, String path) {
        this.name = name;
        this.path = path;
    }

    @Override
    public void run() {
        System.out.println(name);
        FileUtils fileUtils=new FileUtils();
        fileUtils.copyURLFile(path,name);
    }

    public static void main(String[] args){
        TestThread02 testThread01=new TestThread02("11.jpg","https://jiahongdichan.oss-cn-shenzhen.aliyuncs.com/data/2019-01-05/uploads/banner/jpg/59011546679793431.jpg");
        TestThread02 testThread02=new TestThread02("22.jpg","https://jiahongdichan.oss-cn-shenzhen.aliyuncs.com/data/2019-01-05/uploads/banner/jpg/16651546679105625.jpg");
        TestThread02 testThread03=new TestThread02("33.jpg","https://jiahongdichan.oss-cn-shenzhen.aliyuncs.com/data/2019-01-05/uploads/banner/jpg/24221546678934817.jpg");

        testThread01.start();  //開啓線程1
        testThread02.start();  //開啓線程2
        testThread03.start();  //開啓線程3
    }
}

 

  (2)方式2:實現Runable接口

package com.bjsxt.thread;

/**
 * Created by Administrator on 2019/3/9.
 */
public class TestThread03 implements Runnable {

    /**
     * 線程入口
     */
    @Override
    public void run() {
        for (int i=0;i<100;i++){
            System.out.println("一遍聽歌。。。。");
        }
    }

    public static void main(String[] args){
        //創線程實現類
        TestThread03 testThread03=new TestThread03();
        //建立代理對象
        Thread thread=new Thread(testThread03);
        //開啓線程
        thread.start();
        for(int i=0;i<100;i++){
            System.out.println("一遍敲代碼。。。");
        }
    }
}

   案例1:搶票,三個線程共享100張票

  從這個案例能夠看到實現Runable接口實現的多線程,能夠共享資源。而繼承Thread卻不行

package com.bjsxt.thread;

/**
 * Created by Administrator on 2019/3/9.
 */
public class TestThread04 implements Runnable {

    private int ticket=100; //總票數,當前線程共享100個票


    @Override
    public void run() {
        while(true){
            if(ticket<0){
                break;
            }
            try {
                Thread.sleep(100);  //線程睡100毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //獲取當前線程的名字
            System.out.println(Thread.currentThread().getName()+"->"+ticket--);
        }

    }

    public static void main(String[] args){
        TestThread04 testThread04=new TestThread04();
        new Thread(testThread04,"線程1").start(); //開啓線程,該線程的名字爲線程1
        new Thread(testThread04,"線程2").start();
        new Thread(testThread04,"線程3").start();
    }
}

執行結果,咱們看到輸出的有-1,-2,而咱們明明控制的當票數小於0,會退出循環,這就是出現了併發訪問問題。

 

  案例2:龜兔賽跑問題

package com.bjsxt.thread;

/**
 * 模擬龜兔賽跑
 */
public class TestThread05 implements Runnable {

    private String successPerson;   //記錄勝利者
    
    @Override
    public void run() {
        for(int steps=0;steps<=100;steps++){
            System.out.println(Thread.currentThread().getName()+"->"+steps);    //打印當前步數
            boolean flag=isSuccess(steps);
            if(flag){
                break;
            }
        }
    }

    private boolean isSuccess(int steps){
        if(successPerson!=null){
            return true;
        }else{
            if(steps==100){
                successPerson=Thread.currentThread().getName();
                System.out.println(successPerson);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args){
        TestThread05 testThread05=new TestThread05();
        new Thread(testThread05,"wugui").start();
        new Thread(testThread05,"tuzi").start();
    }
}

 

  (3)方式3:實現Callable接口(不重點講,多線程高級纔會用到)

package com.bjsxt.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 模擬龜兔賽跑
 * 第三種實現線程的方式:實現Callable接口,接口的泛型爲call()方法的返回值類型
 */
public class TestThread07 implements Callable<Boolean> {

    private String successPerson;   //記錄勝利者

    @Override
    public Boolean call()throws Exception {
        for(int steps=0;steps<=100;steps++){
            System.out.println(Thread.currentThread().getName()+"->"+steps);    //打印當前步數
            boolean flag=isSuccess(steps);
            if(flag){
                break;
            }
        }
        return true;
    }

    private boolean isSuccess(int steps){
        if(successPerson!=null){
            return true;
        }else{
            if(steps==100){
                successPerson=Thread.currentThread().getName();
                System.out.println(successPerson);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args)throws Exception{
        TestThread07 testThread05=new TestThread07();
        //建立執行服務
        ExecutorService es= Executors.newFixedThreadPool(2);
        //提交執行
        Future<Boolean> future1=es.submit(testThread05);
        Future<Boolean> future2=es.submit(testThread05);
        //獲取結果
        boolean b1=future1.get();
        boolean b2=future2.get();
        //關閉服務
        es.shutdownNow();
    }
}

 

三:線程的狀態

線程有五大狀態,分別是:新生狀態,就緒狀態,運行狀態,阻塞狀態,死亡狀態。

 

 (1)線程中止

  方法1:方法體執行完線程自會中止

  方法2:調用線程(Thread類)提供的stop()/destroy()方法,不推薦,存在問題,已經被廢棄

  方法3:提供一個boolean型的終止變量,當這個變量置爲false,則終止線程的運行(能夠按期去檢查這個變量)

  

package com.bjsxt.thread.state;

/**
 * 中止線程
 *
 */
public class TestThreadStop01 implements Runnable{

    private boolean flag=true;  //當是true線程才執行


    @Override
    public void run() {
        int i=0;
        while(flag){
            System.out.println(Thread.currentThread().getName()+"->"+i++);
        }
    }

    /**
     * 改變標記爲false
     */
    public void myStop(){
        this.flag=false;
    }

    public static void main(String[] args){
        TestThreadStop01 testThreadStop01=new TestThreadStop01();
        new Thread(testThreadStop01,"線程1").start();
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"->"+i);
        }
        testThreadStop01.myStop();  //改變標識,中止線程
    }
}

 

(2)sleep方法

  特色:

    Thread.sleep(1000)當前線程阻塞的毫秒數,sleep可讓線程進入阻塞狀態。

    sleep時間達到後線程進入就緒狀態。

    sleep阻塞線程時,不會釋放鎖。

    sleep()方法是Thread類的靜態方法,會阻塞全部執行該行代碼的線程。

package com.bjsxt;

/**
 * Created by Administrator on 2019/4/8.
 */
public class TestSleep {

    public static void main(String[] args){
        Ticket ticket=new Ticket();
        new Thread(ticket,"線程1").start();
        new Thread(ticket,"線程2").start();
    }

}


class Ticket implements Runnable{

    private int ticketNum=100;  //共有100張票

    @Override
    public void run() {
        while(true){
            if(ticketNum>0){
                try {
                    Thread.sleep(100);
                    System.out.println("第"+--ticketNum+"張票");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                break;
            }
        }

    }
}

(3)yield方法

  特色:

    Thread.yield() 禮讓線程,讓當前正在執行線程暫停,不是阻塞線程,而是將線程從運行狀態轉入就緒狀態,讓cpu調度器從新進行調度

    yield()方法是Thread類的靜態方法,會做用於全部執行該行代碼的線程。

package com.bjsxt;

/**
 * Created by Administrator on 2019/4/8.
 */
public class TestYield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"start......");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"end......");
    }

    public static void main(String[] args){
        TestYield testYield=new TestYield();
        new Thread(testYield,"線程1").start();
        new Thread(testYield,"線程2").start();
    }
}

(4)join方法

  特色:join合併線程,待此線程執行完成後,再執行其餘線程,其餘線程阻塞

       join(long mm)方法能夠傳一個參數毫秒數

      join是成員方法,不是靜態方法,當線程對象調用該方法時,會阻塞執行這段代碼的線程,等到該線程對象執行完成或者時間到了,會讓cpu調度器從新進行調度

package com.bjsxt;

/**
 * Created by Administrator on 2019/4/9.
 */
public class TestJoin {

    public static void main(String[] args){
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<100;i++){
                    System.out.println(Thread.currentThread().getName()+i);
                }
            }
        },"線程a");
        thread.start();

        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<100;i++){
                    System.out.println(Thread.currentThread().getName()+i);
                }
            }
        },"線程b");

        thread2.start();
        for(int i=0;i<100;i++){
            if(i==20){
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

(5)獲取線程的狀態

  

package com.bjsxt;

/**
 * 測試線程狀態
 */
public class TestThreadState {

    public static void main(String[] args){

        Thread thread=new Thread(()->{
            for(int i=0;i<10;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread.State state=thread.getState();   //獲取該線程的當前狀態
        System.out.println(state);
        thread.start();
        System.out.println(thread.getState());


    }

}

四:線程的優先級 

Java提供一個線程調度器來監控程序中啓動後進入就緒狀態的全部線程。線程調度器按照線程的優先級決定應調度哪一個線程來執行。
線程的優先級用數字表示,範圍從1到10
• Thread.MIN_PRIORITY = 1
• Thread.MAX_PRIORITY = 10
• Thread.NORM_PRIORITY = 5
使用下述方法得到或設置線程對象的優先級。
• int getPriority();
• void setPriority(int newPriority);
優先級的設定建議在start()調用前
注意:優先級低只是意味着得到調度的機率低。並非絕對先調用優先級高後調用優先級低的線程。

package com.bjsxt;

import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;

/**
 * 測試線程優先級
 */
public class TestPriority {

    public static void main(String[] args){
        MyPriority myPriority=new MyPriority();
        Thread thread1=new Thread(myPriority,"線程a");
        Thread thread2=new Thread(myPriority,"線程b");
        Thread thread3=new Thread(myPriority,"線程c");
        Thread thread4=new Thread(myPriority,"線程d");
        Thread thread5=new Thread(myPriority,"線程e");
        thread1.setPriority(10);
        thread2.setPriority(9);
        thread3.setPriority(8);
        thread4.setPriority(2);
        thread5.setPriority(1);
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
    }

}

class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+Thread.currentThread().getPriority());
    }
}

五:用戶線程和守護線程

• 線程分爲用戶線程和守護線程;
• 虛擬機必須確保用戶線程執行完畢;
• 虛擬機不用等待守護線程執行完畢;
• 如後臺記錄操做日誌、監控內存使用等

package com.bjsxt;

/**
 * 手動建立的線程默認都是用戶線程
 */
public class TestThreadType {
    public static void main(String[] args){
        Thread thread=new Thread(new God());
        thread.setDaemon(true); //把該線程設置爲守護線程
        thread.start();
        new Thread(new You()).start();
    }

}

class You implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("用戶線程"+i);
        }
    }
}

class God implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("守護線程在運行。。。");
        }
    }
}

 六:線程中的其餘方法

isAlive()   判斷線程是否還活着,即線程是否還未終止setName()  給線程起一個名字getName()  獲取線程的名字currentThread() 取得當前正在運行的線程對象,也就是獲取本身自己

相關文章
相關標籤/搜索