多線程是指機器支持在同一時間執行多個線程,可以提升cpu的利用率 ,提升程序的執行效率。java
(1)繼承Thread類spring
多線程能夠經過繼承Thread類並從新Thread的run方法來啓動多線程。而後經過Thread的start方法來啓動線程。上代碼:springboot
package com.wangx.thread.t1; public class Demo1 extends Thread { Demo1(String name) { super(name); } @Override public void run() { while (!interrupted()) { System.out.println("線程" + Thread.currentThread().getName() + "執行了。。。。"); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { Demo1 demo1 = new Demo1("one"); Demo1 demo2 = new Demo1("two"); demo1.start(); demo2.start(); demo1.interrupt(); } }
這裏也順便用了線程的中斷,當但願一個線程再也不執行時,就是用interrupt()方法來進行中斷,此時的的線程對象將不會再執行,interrupted()方法判斷線程是否中斷,返回boolean值,噹噹前線程被中斷時返回false,使用interrupted()方法能夠避免報InterruptedException異常。多線程
(2)實現Runnable接口異步
Runnable接口中只有一個run方法,實現Runnable接口的run方法做爲任務處理方法,將Runnable的實現類對象傳入到Thread的構造中,建立線程,並使用Thread.start()啓動線程。代碼:ide
package com.wangx.thread.t1; public class Demo2 implements Runnable { /** * 重寫run方法 */ @Override public void run() { System.out.println("執行了"); } public static void main(String[] args) { Demo2 demo2 = new Demo2(); //將demo2傳入到Thread中 Thread thread = new Thread(demo2); Thread thread1 = new Thread(demo2); thread.start(); thread1.start(); } }
這裏的Runnable接口實際上是做爲一個線程任務處理器,查看Thread中的run方法和構造方法能夠看出,但傳入的target(Runnable對象)不爲空時,執行Runnable的run方法,因此經過啓動線程時實際先執行的仍是Thread中的run方法去調用target的run方法。Thread中的run()方法源碼以下:性能
public void run() { if (target != null) { target.run(); } }
(3)匿名內部類方法學習
匿名內部類其實跟繼承Thread類並重寫run方法原理同樣,都是經過覆蓋父類run方法,執行當前對象的run方法的方式來執行只須要處理的任務,只是寫法上跟簡潔,而且只會執行一次,若是任務只須要執行一次時而且減小代碼量時可使用,代碼以下:spa
package com.wangx.thread.t1; public class Demo3 { public static void main(String[] args) { //匿名內部類啓動多線程 new Thread(){ @Override public void run() { System.out.println("Thread in running"); } }.start(); } }
還能夠經過Runnable的匿名內部類來實現,原理與第二點一致,代碼以下:線程
package com.wangx.thread.t1; public class Demo3 { public static void main(String[] args) { //匿名內部類啓動多線程 new Thread(new Runnable() { @Override public void run() { System.out.println("runnable thread is running"); } }).start(); } }
在這裏須要注意的時,當同時使用Thread的匿名內部類和Runnable接口的匿名內部類同時使用時,此時執行的是Thread的run方法,而不會執行Runnable的run方法,緣由是根據源碼能夠看出此時run方法已經被重寫,因此不會調用target.run語句,因此Runnable的run方法不會執行。代碼:
package com.wangx.thread.t1; public class Demo3 { public static void main(String[] args) { //Thread匿名內部類和Runnable匿名內部類同時存在時,打印sub is running new Thread(new Runnable() { @Override public void run() { System.out.println("Runnable"); } }){ @Override public void run() { System.out.println("sub is running"); } }.start(); } }
(4)建立帶返回值的線程
實現Callable<T>泛型方法,重寫call方法,泛型傳入什麼類型的值,call就返回什麼類型的值,並使用FutureTask接收Callable對象,FutureTask的泛型爲Callable中傳入的類型,將
FutrueTask對象傳入到Thread中啓動線程,並使用FutureTask的get方法獲取到call方法的返回值,代碼以下:
package com.wangx.thread.t1; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Demo4 implements Callable<Integer> { public static void main(String[] args) throws ExecutionException, InterruptedException { Demo4 demo4 = new Demo4(); //經過demo4建立task對象 FutureTask<Integer> task = new FutureTask<>(demo4); //經過task建立並啓動線程 Thread thread = new Thread(task); thread.start(); Integer result = task.get(); System.out.println("計算結果爲:" + result); } @Override public Integer call() throws Exception { System.out.println("正在進行緊張的計算...."); Thread.sleep(1000); return 1; } }
Thread中能夠接接收FutureTask是由於FutureTask是Runnable的實現類,因此也能夠說FutureTask是Runnable的另類實現。
(5)線程池的方式
因爲線程的建立和銷燬都會消耗過多的內存資源,因此在jdk5以後JAVA新增了線程池的概念,ThreadPoolExecutor是線程池的核心類,它重載了不少的構造方法讓咱們構造一個線程池,在線程池中建立指定多個的線程,執行任務時,將從線程池中取出線程去執行任務,任務執行完成後將線程歸還到線程池中。線程池不用頻繁的建立和銷燬線程池,減小了資源的消耗,提升了性能,能夠直接經過new ThreadPoolExecutor()來指定本身建立線程池,也可使用Executors中的靜態方法來建立各類類型的線程池,這裏先建立一個制定大小的線程池,代碼以下:
package com.wangx.thread.t1; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Demo6 { public static void main(String[] args) { //建立線程池大小爲10的線程池 ExecutorService executor = Executors.newFixedThreadPool(10); //循環執行100次,打印線程名字,發現老是隻用10個線程在執行 for (int i = 0; i < 100; i++){ executor.execute(()-> { System.out.println(Thread.currentThread().getName()); }); } executor.shutdown(); } }
executor.execute()傳入Runnable對象,執行任務,這裏使用lambda表達式寫法,其實就是一個Runnable匿名對象,打印當前線程名稱。當任務100次循環以後咱們發現程序並無結束,
這是由於線程池仍然存活,此時調用executor.shutdown();方法關閉線程池,結束程序。由於Executors中的靜態方法也是經過ThreadPoolExecutor來建立的,因此這裏就不寫直接建立
的案例了,關於ThreadPoolExecutor構造方法的個參數做用下一節將會詳解。
(6)在spring3+中使用多線程
spring3以後的版本提供了對多線程的支持,這裏案例是spring boot項目爲例的,方便引入spring的依賴。
在spring boot中使用多線程須要使用@EnableAsync註解來開啓異步執行的支持,而後在須要執行的方法上加上@Async標記該方法爲異步方法,以後直接經過bean調用該方法能夠發現該方法是
異步執行的,實例代碼爲:
package com.example.springthread; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableAsync; /** *springboot啓動類,這裏也用作了配置類 */ @SpringBootApplication //開啓註解 @EnableAsync public class SpringthreadApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(SpringthreadApplication.class, args); DemoService demoService = context.getBean(DemoService.class); demoService.a(); demoService.b(); } } /*******************異步方法所在的bean*************************/ package com.example.springthread; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class DemoService { //標記方法爲異步方法 @Async public void a() { while (true){ System.out.println("a is running"); } } @Async public void b() { while (true){ System.out.println("b is running"); } } }
啓動springboot,調用DemoService的a/b方法,能夠看到他們異步執行。這裏只是爲了方便引入spring的依賴,直接使用spring也是能夠實現異步方法調用的spring也支持計劃任務,使用@EnableScheduling開啓計劃任務,@Scheduled標記計劃任務的方法,在註解中傳入相應的執行時間和週期便可實現計劃任務
(7)建立定時任務
在java.util包中提供了一個Timer類能夠用來建立定時任務,能夠指定某個時間開始執行,以後每隔多長時間執行一次。代碼以下:
package com.wangx.thread.t1; import java.util.Timer; import java.util.TimerTask; public class Demo5 { public static void main(String[] args) { Timer timer = new Timer(); // 建立1秒後開始中,沒隔1秒執行一次的定時任務 timer.schedule(new TimerTask() { @Override public void run() { System.out.println("timer task is running"); } },1000, 1000); } }
Timer接收一個TimerTask爲要執行的任務,其他參數爲須要執行的時間方式,能夠進入Timer的源碼查看個參數的意義,構建本身想要的計劃任務。
本章主要是爲了回顧java生態中各類多線程的實現方式,並不涉及太多概念和原理問題,具體的原理將會在後面的學習中逐漸補上。