線程的創建方式一
/**
* 從創建多線程的方式一
* 1.創建一個類繼承Thread
* 2.重寫Thread類的run方法-方法體的內容就是你這個線程想要完成的任務
* 3.在主方法中new類的對象
* 4.調用這個對象的start方法
* 此時主線程main在運行,我們自定義的線程也在運行
*/
package com.eight;
/**
* 從創建多線程的方式一
* 1.創建一個類繼承Thread
* 2.重寫Thread類的run方法-方法體的內容就是你這個線程想要完成的任務
* 3.在主方法中new類的對象
* 4.調用這個對象的start方法
* 此時主線程main在運行,我們自定義的線程也在運行
*/
public class Demo1 {
public static void main(String[] args) {
//這是第一個線程
MyThread t1 = new MyThread();
t1.setName("線程1");
t1.start();
//這是第二個線程
MyThread t2 = new MyThread();
t2.setName("線程2");
t2.start();
System.out.println("hello");
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i=1;i<=100;i++){
if (i%2 == 0)
System.out.println(this.getName()+":"+i);
}
}
}
線程的創建方式二
/**
* 創建多線程的方式2
* 1.創建一個類ThreadA 實現接口Runnable
* 2.ThreadA中實現run方法
* 3.主線程中new ThreadA的對象a
* 4.t1 = new Thread(a),將a傳進去
* 5.調用t1的start方法
*/
package com.eight;
/**
* 創建多線程的方式2
* 1.創建一個類ThreadA 實現接口Runnable
* 2.ThreadA中實現run方法
* 3.主線程中new ThreadA的對象a
* 4.t1 = new Thread(a),將a傳進去
* 5.調用t1的start方法
*/
class ThreadA implements Runnable{
@Override
public void run() {
for (int i=1;i<=100;i++){
if (i%2 == 0)
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class Demo2 {
public static void main(String[] args) {
ThreadA a = new ThreadA();
//這是第一個線程
Thread t1 = new Thread(a);
t1.setName("線程1");
t1.start();
//這是第二個線程
Thread t2 = new Thread(a);
t2.setName("線程2");
t2.start();
}
}
線程的生命週期
線程的同步機制
面試題:synchronized 與 Lock的異同?
相同:二者都可以解決線程安全問題
不同:synchronized機制在執行完相應的同步代碼以後,自動的釋放同步監視器,Lock需要手動的啓動同步(lock(),同時結束同步也需要手動的實現(unlock())
線程通信
線程通信涉及到的三個方法:
* wait():一旦執行此方法,當前線程就進入阻塞狀態,並釋放同步監視器。
* notify():一旦執行此方法,就會喚醒被wait的一個線程。如果有多個線程被wait,就喚醒優先級高的那個。
* notifyAll():一旦執行此方法,就會喚醒所有被wait的線程。
面試題:sleep() 和 wait()的異同?
相同點:一旦執行方法,都可以使得當前的線程進入阻塞狀態。
不同點:1)兩個方法聲明的位置不同:Thread類中聲明sleep() , Object類中聲明wait()
2)調用的要求不同:sleep()可以在任何需要的場景下調用。 wait()必須使用在同步代碼塊或同步方法中
3)關於是否釋放同步監視器:如果兩個方法都使用在同步代碼塊或同步方法中,sleep()不會釋放鎖,wait()會釋放鎖。
JDK5新增的線程創建方式——實現Callable接口
實現Callable接口。 --- JDK 5.0新增
//1.創建一個實現Callable的實現類
class NumThread implements Callable{
//2.實現call方法,將此線程需要執行的操作聲明在call()中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
if(i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.創建Callable接口實現類的對象
NumThread numThread = new NumThread();
//4.將此Callable接口實現類的對象作爲傳遞到FutureTask構造器中,創建FutureTask的對象
FutureTask futureTask = new FutureTask(numThread);
//5.將FutureTask的對象作爲參數傳遞到Thread類的構造器中,創建Thread對象,並調用start()
new Thread(futureTask).start();
try {
//6.獲取Callable中call方法的返回值
//get()返回值即爲FutureTask構造器參數Callable實現類重寫的call()的返回值。
Object sum = futureTask.get();
System.out.println("總和爲:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
如何理解實現Callable接口的方式創建多線程比實現Runnable接口創建多線程方式強大?
* 1. call()可以返回值的。
* 2. call()可以拋出異常,被外面的操作捕獲,獲取異常的信息
* 3. Callable是支持泛型的
JDK5新增的線程創建方式——線程池技術
public class ThreadPool {
//主方法
public static void main(String[] args) {
//1. 提供指定線程數量的線程池
ExecutorService service = Executors.newFixedThreadPool(10);
//這個ExecutorService 其實是一個接口,所以service的實現類是ThreadPoolExecutor,如下是可以強轉的
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//設置線程池的屬性
//System.out.println(service.getClass());
//service1.setCorePoolSize(15);
//service1.setKeepAliveTime();
//2.執行指定的線程的操作。需要提供實現Runnable接口或Callable接口實現類的對象
//線程池只是給我們提供一個一個的線程,具體線程做什麼任務還需要我們自己編寫
service.execute(new NumberThread());//適合適用於Runnable
service.execute(new NumberThread1());//適合適用於Runnable
//service.submit(Callable callable);//適合使用於Callable
//3.關閉連接池
service.shutdown();
}
}
class NumberThread implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}