併發面試題:java中有幾種方法能夠實現一個線程?

建立並啓動線程的6種方式java

  • 繼承Thread類建立線程
  • 實現Runnable接口建立線程
  • 使用Callable和FutureTask建立線程
  • 使用線程池,例如用Executor框架
  • Spring實現多線程(底層是線程池)
  • 定時器Timer (底層封裝了一個TimerThread對象)

一、繼承Thread類建立線程

1.1繼承Thread類方式建立線程的實現步驟:spring

步驟:數據庫

1) 定義一個類A繼承於java.lang.Thread類設計模式

2)在A類中覆蓋Thread類中的run方法緩存

3)咱們在run方法中編寫須要執行的操做---->run方法裏的,線程執行體網絡

4)在main方法(線程)中,建立線程對象,並啓動線程數據結構

  • 建立線程類對象: A類 a = new A類();
  • 調用線程對象的start方法: a.start();//啓動一個線程

注意:千萬不要調用run方法,若是調用run方法比如是對象調用方法,依然仍是隻有一個線程,並無開啓新的線程。多線程

1.2需求:使用兩個線程實現邊聽歌邊打遊戲框架

實現代碼:異步

//音樂線程
public class MusicThread {
public static void main(String[] args) {
//建立遊戲線程對象
GameThread game = new GameThread();
//啓動遊戲線程
game.start();
while(true){
System.out.println(Thread.currentThread().getName()+"聽音樂!");
}
}
}
//遊戲線程
class GameThread extends Thread{
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName()+"打遊戲!");
}
}
}

注意:有的小夥伴可能以爲音樂線程沒有啓動,在這裏其實音樂線程已經啓動起來了,而啓動音樂線程的對象就是咱們的JVM,此處main方法其實啓動的時候會建立一個主線程去執行main方法,因此我在這裏使用主線程做爲了個人音樂線程。

二、實現Runnable接口建立線程

2.1實現Runnable接口方式建立線程的實現步驟:

1)定義一個類A實現於java.lang.Runnable接口,注意A類不是線程類。
2)在A類中覆蓋Runnable接口中的run方法。
3) 咱們在run方法中編寫須要執行的操做---->run方法裏的,線程執行體。
4)在main方法(線程)中,建立線程對象,並啓動線程。

  • 建立線程類對象: Thread t = new Thread(new A());
  • 調用線程對象的start方法: t.start();

2.2需求:使用兩個線程實現邊聽歌邊打遊戲

實現代碼:

//音樂線程
public class MusicThread {
public static void main(String[] args) {
//建立遊戲線程對象
Thread game = new Thread(new Game());
//啓動遊戲線程
game.start();
while(true){
System.out.println(Thread.currentThread().getName()+"聽音樂!");
}
}
}


//遊戲
class Game implements Runnable{
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName()+"打遊戲!");
}
}
}

2.3 繼承方式和實現方式的區別

1)繼承方式是一個類繼承了Thread後成爲線程類的子類,實現方式是一個類實現Runnable接口,可是這個類不是線程類,由於該類沒有start等方法。

2)啓動的時候繼承方式直接調用本身的start方法,實現方式是藉助了Thread中的start方法啓動的,自身沒有start方法。

3)繼承方式調用的run方法是經過方法覆蓋,經過繼承方式實現的,運行的時候先找子類,沒有最後才運行父類的run方法。實現方式是執行Thread的run方法,而Thread中的run方法調用了實現類中的run方法,使用過組合關係的方法調用實現的。

三、實現 Callable 接口

3.1使用Callable和FutureTask建立線程的實現步驟:

1)定義一個Callable接口的實現類

2)建立Callable實現類對象傳遞給FutureTask構造器

3)將FutureTask對象傳遞給Thread構造器

4)Thread對象調用start方法啓動線程

5)經過FutureTask對象的get方法獲取線程運行的結果

注意:
Future就是對於具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、獲取結果。必要時能夠經過get方法獲取執行結果,該方法會阻塞直到任務返回結果。

使用場景:使用多線程計算結果並返回該結果。

3.2需求:使用2個線程異步計算1-1000,000內之和

實現代碼:

public class CallableDemo {
public static void main(String[] args) throws Exception  {
//1.建立並啓動線程
Callable<Integer> call1 = new CallableImpl(0, 50000);
Callable<Integer> call2 = new CallableImpl(50001, 100000);


FutureTask<Integer> f1 = new FutureTask<>(call1);
FutureTask<Integer> f2 = new FutureTask<>(call2);


new Thread(f1).start();
new Thread(f2).start();
//2.獲取每個線程的結果
int ret1 = f1.get();
int ret2 = f2.get();
int ret= ret1+ret2;
System.out.println(ret);
}
}
class CallableImpl implements Callable<Integer>{


private int min;
private int max;


public CallableImpl(int min, int max) {
this.min = min;
this.max = max;
}


@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = min; i <= max; i++) {
sum+=i;
}
return sum;
}
}

3.3Callable和Runnable的區別以下:

Callable定義的方法是call,而Runnable定義的方法是run。

Callable的call方法能夠有返回值,而Runnable的run方法不能有返回值。

Callable的call方法可拋出異常,而Runnable的run方法不能拋出異常。

注意:

    FutureTask爲Runnable的實現類

    FutureTask能夠視爲一個閉鎖(門閂),由於只有當線程運行完纔會出現結果。

四、使用線程池

線程池,顧名思義就是一個池子裏面放了不少的線程,咱們用就將線程從裏面拿出來,使用完畢就放回去池子中。設計和數據庫鏈接池類似,存在靜態工廠方法用於建立各類線程池。

操做步驟:

1)使用Executors工具類中的靜態工廠方法用於建立線程池
      newFixedThreadPool:建立可重用且固定線程數的線程池,
      newScheduledThreadPool:建立一個可延遲執行或按期執行的線程池
      newCachedThreadPool:建立可緩存的線程池

2)使用execute方法啓動線程

3)使用shutdown方法等待提交的任務執行完成並後關閉線程。

代碼演示以下:

public class Demo4 {
    public static void main(String[] args) {
        Executor executor = Executors.newFixedThreadPool(5);
        executor.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
        ((ExecutorService) executor).shutdown();
    }
}

五、Spring實現多線程

在Spring3以後,Spring引入了對多線程的支持,若是你使用的版本在3.1之前,應該仍是須要經過傳統的方式來實現多線程的。從Spring3同時也是新增了Java的配置方式,並且Java配置方式也逐漸成爲主流的Spring的配置方式。

代碼演示以下:

導入的包:

<dependencies>
     <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
          <version>2.1.0.RELEASE</version>
     </dependency>
</dependencies>

配置類:

@Configuration
@ComponentScan("cn.wolfcode")
@EnableAsync //容許使用異步任務
public class SpringConfig {}

服務類:

@Service
public class SpringService {
    @Async // 這裏進行標註爲異步任務,在執行此方法的時候,會單獨開啓線程來執行
    public void dowork1() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
    @Async
    public void dowork2() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}

測試類:

public class SpringThreadDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        SpringService bean = context.getBean(SpringService.class);
        bean.dowork1();
        bean.dowork2();
    }
}

注意:此時會出現一個DEBUG信息

file

在這裏DEBUG信息不是什麼錯誤,不會影響代碼的正常運行,其實能夠不用管的,可是爲何出現這個問題呢?

Spring的定時任務調度器會經過BeanFactory.getBean的方法來嘗試獲取一個註冊過的TaskExecutor對象來作任務調度,獲取不到TaskExecutor對象再嘗試找ScheduledExecutorService 對象,都找不到就報DEBUG信息。報錯以後就找本身自己默認的scheduler定時器對象,這個舉動實際上是作一個提醒做用,因此若是沒有強迫症能夠不用管它。

解決Spring使用多線程的報錯信息

強迫症患者想要解決怎麼辦,三種方式:

  • 在log4j文件中加入log4j.logger.org.springframework.scheduling = INFO(治標不治本)
  • 在本配置文件或者配置類中設置一個bean
  • 配置類實現AsyncConfigurer接口並覆蓋其getAsyncExecutor方法

6 定時器

嚴格來講定時器(Timer)不是線程,他只是調度線程的一種工具,它裏面封裝了一個線程,因此咱們可使用定時器來使用線程。

file

file

操做步驟:

1)建立Timer 對象

2)調用schedule方法

3)傳入TimerTask子類對象

代碼演示以下:

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }


    }
}, 100, 100);
文源網絡,僅供學習之用,若有侵權,聯繫刪除。

我將優質的技術文章和經驗總結都聚集在了個人公衆號【Java圈子】裏。

爲方便你們學習,我還整理了一套學習資料,涵蓋Java虛擬機、spring框架、Java線程、數據結構、設計模式等等,免費提供給熱愛Java的同窗~

file

相關文章
相關標籤/搜索