天天看點

java進階程式設計——多線程

一、最常見的建立線程的兩種方式

1.繼承Thread

package com.xioayu.threadTest1;

/**
 * Created by hasee on 2019/5/26.
 */
public class MyThread1 extends Thread {

    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        MyThread1 t2 = new MyThread1();

        t1.setName("線程1");
        t2.setName("線程2");

        t1.start();
        t2.start();
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
           

【詳解】繼承Thread類,重寫父類的run()方法,然後執行一系列操作。程式首先從mai()方法進入,然後通過new  MyThread1()建立了兩個對象,調用setName()可以修改線程名,調用start()方法可以讓線程被開啟。run()方法會被回調,其中currentThread()是靜态方法,sleep()方法也是Thread的靜态方法,代碼裡的sleep()其實就是this.sleep(),而this就是該方法被調用的對象,很顯然是Thread.sleep();

2、實作Runnable接口

package com.xioayu.threadTest1;

/**
 * Created by hasee on 2019/5/26.
 */
public class MyThread2 implements Runnable {

    public static void main(String[] args) {
        MyThread2 t1 = new MyThread2();
        MyThread2 t2 = new MyThread2();

        Thread thread1=new Thread(t1);
        Thread thread2=new Thread(t2);

        thread1.setName("線程1");
        thread2.setName("線程2");

        thread1.start();
        thread2.start();
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
           

【詳解】第二種方式是通過實作Runnable接口,重寫run()方法,而我們還是需要newThread(),調用start()方法開啟線程。這裡的sleep()前面需要加Thread,因為此時的run()是new Thread()這個對象調用的,this指向的是new Thread()這個對象

java進階程式設計——多線程

【運作結果】

java進階程式設計——多線程

二、線程的生命周期

java進階程式設計——多線程

建立了線程之後,調用start()方法,線程進入就緒狀态,隻有當線程獲得了CPU,就會到達運作狀态,此時run()方法就會被執行,可能還沒執行完,失去了CPU,就會回到就緒狀态,等待下次擷取到CPU之後繼續執行run()方法。這就是為什麼上述例子兩個子線程互動執行for循環。通過調用sleep()方法可以讓線程進入阻塞狀态,等待時間到後又回到就緒狀态。

三、給線程加鎖

簡單介紹java的記憶體配置設定,jvm記憶體主要包括

棧:儲存局部變量的值,包括:1.用來儲存基本資料類型的值;2.儲存類的執行個體,即堆區對象的引用(指針)。也可以用來儲存加載方法時的幀。

 堆:用來存放動态産生的資料,比如new出來的對象。注意建立出來的對象隻包含屬于各自的成員變量,并不包括成員方法。因為同一個類的對象擁有各自的成員變量,存儲在各自的堆中,但是他們共享該類的方法,并不是每建立一個對象就把成員方法複制一次。

方法區:包含如下内容:

 常量池:JVM為每個已加載的類型維護一個常量池,常量池就是這個類型用到的常量的一個有序集合。包括直接常量(基本類型,String)和對其他類型、方法、字段的符号引用(1)。池中的資料和數組一樣通過索引通路。由于常量池包含了一個類型所有的對其他類型、方法、字段的符号引用,是以常量池在Java的動态連結中起了核心作用。常量池存在于方法區中。

代碼段:用來存放從硬碟上讀取的源程式代碼。

資料段:用來存放static定義的靜态成員。

》》其中每個線程都會有一個屬于自己的棧,而堆、方法區都是所有線程共享的,這就存在資料同步的問題。比如在售票的時候,多個視窗去共享同一個資料,這個時候就會出現重票或負票的情況。線程在共享資料方面通過加鎖來實作線程同步,就比如很多人排隊上廁所,然後一個一個的進去,進去一個鎖一下門,等他出來後把鎖交給另一個人。

實作線程同步的方法有兩種:

1.同步代碼塊

2.同步方法

1.實作Runnable的同步代碼塊

package com.xioayu.threadTest1;

/**
 * Created by hasee on 2019/5/24.
 */
public class ThreadTest implements Runnable{
/**
 * 線程同步:
 * 1.同步代碼塊synchronized(同步螢幕:鎖){
 *     //需要同步的代碼
 * }
 * 2.同步方法
 *
 */

//任何對象都能充當鎖
Object obj=new Object();
public  int ticket=100;
    @Override
    public void run() {
        while (true){
            synchronized (obj) {//synchronized (this) {
                if(ticket>0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        ThreadTest t1=new ThreadTest();
        new Thread(t1).start();
        new Thread(t1).start();
        new Thread(t1).start();
    }
}
           

2.實作Runnable的同步代碼塊

package com.xioayu.threadTest1;

/**
 * Created by hasee on 2019/5/24.
 */
public class ThreadTest2 implements Runnable{
/**
 * 線程同步:
 * 1.同步代碼塊synchronized(同步螢幕:鎖){
 *     //需要同步的代碼
 * }
 * 2.同步方法
 *
 */

public  int ticket=100;
    @Override
    public void run() {
        while (true){
            Boolean b=reduce();
              if(b){
                  break;
              }
        }
    }

    public synchronized Boolean reduce(){
        if(ticket>0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
            ticket--;
        }else {
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        ThreadTest2 t1=new ThreadTest2();
        new Thread(t1).start();
        new Thread(t1).start();
        new Thread(t1).start();
    }
}
           

3.繼承Thread的同步代碼塊

package com.xioayu.threadTest1;

/**
 * Created by hasee on 2019/5/24.
 */
public class ThreadTest3 extends Thread {

    public static int ticket = 100;

    public static void main(String[] args) {
        ThreadTest3 t1 = new ThreadTest3();
        ThreadTest3 t2 = new ThreadTest3();
        ThreadTest3 t3 = new ThreadTest3();
        t1.start();
        t2.start();
        t3.start();

    }

    @Override
    public void run() {
        while (true) {
            synchronized (ThreadTest3.class) {//synchronized (obj) {
                if (ticket > 0) {
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
                    ticket--;
                } else {
                    break;
                }
            }

        }
    }
}
           

4.繼承Thread的同步代碼塊

package com.xioayu.threadTest1;

/**
 * Created by hasee on 2019/5/24.
 */
public class ThreadTest4 extends Thread {

    public static int ticket = 100;

    public static void main(String[] args) {
        ThreadTest4 t1 = new ThreadTest4();
        ThreadTest4 t2 = new ThreadTest4();
        ThreadTest4 t3 = new ThreadTest4();
        t1.start();
        t2.start();
        t3.start();

    }

    @Override
    public void run() {
        while (true) {
            Boolean b=ThreadTest4.reduce();
            if(b){
                break;
            }

        }
    }

    public static synchronized boolean reduce() {
        if (ticket > 0) {
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
            ticket--;
        }else {
            return true;
        }
        return false;
    }
}
           

四、讓線程堵塞和喚醒線程

package com.xioayu.threadTest2;

/**
 * Created by hasee on 2019/5/24.
 */
public class Number implements Runnable {
    public  int ticket=100;
    @Override
    public void run() {
        while (true){
            synchronized (this) {
                notifyAll();
                if(ticket>0) {
                    System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
                    ticket--;
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        Number t1=new Number();
        new Thread(t1).start();
        new Thread(t1).start();
    }
}
           

【結果】

java進階程式設計——多線程

wait():讓線程進入堵塞狀态,并釋放鎖,隻要不喚醒,一直在堵塞狀态。Object類中申明,隻能在同步方法塊内調用

sleep() :讓線程進入堵塞狀态,不釋放鎖,時間結束後自動進入就緒狀态,就是其他線程搶占了CPU也得不到鎖,隻有等該線程再次得到CPU執行結束或者釋放鎖後其他線程開可能得到鎖。Thread類中申明,任何場景都可調用

notify()和notifyAll():喚醒線程,Object類中申明

生成者消費者例子

package com.xioayu.threadTest2;

/**
 * Created by hasee on 2019/5/25.
 */
public class ThreadTest5 {

    public static void main(String[] args) {
        Apple apple=new Apple();
        Consumer c1=new Consumer(apple);
        Producer p1=new Producer(apple);
        Producer p2=new Producer(apple);

        Thread t1=new Thread(c1);
        Thread t2=new Thread(p1);
        Thread t3=new Thread(p2);
        t1.setName("生成者1");
        t2.setName("消費者1");
        t3.setName("消費者2");

        t1.start();
        t2.start();
        t3.start();
    }
}

class Consumer implements Runnable{

    private Apple apple;

    Consumer(Apple apple){
        this.apple=apple;
    }

    @Override
    public void run() {
          while (true){
              try {
                  Thread.sleep(10);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              apple.reduce();
          }
    }


}

class Producer implements Runnable{
    private Apple apple;

    Producer(Apple apple){
        this.apple=apple;
    }
    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            apple.add();
        }
    }


}

class Apple{
    private int number=0;

    public synchronized void add(){
       if(number<5){
           number++;
           System.out.println("已供應第"+number+"個蘋果");
           notifyAll();
       }else{
           try {
               wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
    }

    public synchronized void reduce(){
        if(number>0){
            System.out.println("已消費第"+number+"個蘋果");
            number--;
            notifyAll();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
           

五、未來任務

這是JDK5.0之後新增的一種建立線程的方式,讓原本傳回值為void的線程有了傳回值,便于線程之間的通信

package com.xiaoyu.threadTest3;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * Created by hasee on 2019/5/25.
 */
public class FutureTaskTest {
    public static void main(String[] args) {
        MyCallable myCallable = new MyCallable();
        FutureTask<List<User>> listFutureTask = new FutureTask<>(myCallable);
        Thread thread = new Thread(listFutureTask);
        thread.setName("子線程");
        thread.start();

        try {
            List<User> list=listFutureTask.get();
            for(User user:list){
                System.out.println(user.getUsername()+"---"+user.getPassword());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable<List<User>>{

    @Override
    public List<User> call() throws Exception {
        List<User> list=new ArrayList<User>();
        for (int i=0;i<10;i++) {
            Thread.sleep(100);
            User user = new User();
            user.setUsername(i+"");
            user.setPassword(i+"");
            list.add(user);
        }
        return list;
    }
}


class User{
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
           

結果

java進階程式設計——多線程

六、線程池

package com.xioayu.threadTest2;

import java.util.concurrent.*;

/**
 * Created by hasee on 2019/5/25.
 */
public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
       /* ThreadPoolExecutor service1= (ThreadPoolExecutor) service;
        service1.setCorePoolSize(5);
        service1.setKeepAliveTime();
        service1.setMaximumPoolSize(15);*/
        service.execute(new MyThread());
        Future<Integer> submit = service.submit(new MyThread2());
        try {
            Integer integer = submit.get();
            System.out.println("傳回值"+integer);

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        service.shutdown();
    }
}

class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i=0;i<100;i=i+2){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }


}

class MyThread2 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        for (int i=1;i<100;i=i+2){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
        return 3;
    }
}
           

execute:實作Runnable接口

submit:實作Callable接口