线程创建的四种方法
线程创建的四种方法:
创建线程的常用四种方法:
第一种 继承Thread类
第二种 实现Runnable接口
第三种 实现Callable接口
第四种 通过线程池如用Executor框架
创建线程的常用四种方法:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 线程池方法创建如用Executor框架
通过继承Thread类或者Runnable接口、Callable接口都可以实现多线程,不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常而已。
采用实现Runnable、Callable接口的方式创建线程的优缺点:
优点:线程类只是实现了Runnable或者Callable接口,还可以继承其他类。这种方式下,多个线程可以共享一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。
缺点:编程稍微复杂一些,如果需要当我当前该线程,则必须使用Thread.currentThread()方法
采用继承Thread类的方法创建线程的优缺点:
优点:编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获取当前线程
缺点:因为线程类已经继承了Thread类,Java语言是单继承的,所以不能再继承其他父类。
采用线程池创建线程的优点:
1、提高响应速度----减少了创建新线程的时间
2、降低资源消耗----重复利用线程池中线程,不需要每次都创建
3、便于线程管理
第一种 继承Thread类
通过继承Thread类来创建并启动多线程的一般步骤如下
1、定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2、创建Thread子类的实例,也就是创建了线程对象
3、启动线程,即调用线程的start()方法
//1. 创建一个继承于Thread类的子类
class MyThread extends Thread{// 2. 重写Tread类的run()@Overridepublic void run() {for (int i = 0; i <100 ; i++) {if(i%2==0){// System.out.println(i);//输出线程名System.out.println(Thread.currentThread().getName()+":"+i);}}}
}
public class ThreadTest {public static void main(String[] args) {//3. 创建Tread类的子类的对象MyThread t = new MyThread();//4. 通过此对象调用start(): A :启动当前线程 B: 调用当前线程的run()方法t.start();// 第一个问题: 这是调方法 还是在主线程中执行的 我们不能直接调用run()的方式启动线程// t.run();//第二个问题:再启动一个线程 不可以让已经start()了的线程去执行 不然会报错IllegalThreadStateException// t.start();MyThread t2 = new MyThread();t2.start();// 证明线程有交互性for (int j = 0; j < 50; j++) {if(j%2!=0){System.out.println(Thread.currentThread().getName()+":"+j+"*****");}}//创建Thread类的匿名子类的方式new Thread(){@Overridepublic void run() {for (int k = 0; k <10 ; k++) {System.out.println(Thread.currentThread().getName()+": LP");}}}.start();}
}
运行部分截图:
第二种 实现Runnable接口
通过实现Runnable接口创建并启动线程一般步骤如下:
1、定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
2、创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
3、第三部依然是通过调用线程对象的start()方法来启动线程
// 1、创建一个类实现Runnable接口
class Number implements Runnable{// 2. 重写Runnable接口的run()@Overridepublic void run() {for (int i = 0; i <100 ; i++) {if(i%2==0){// System.out.println(i);//输出线程名System.out.println(Thread.currentThread().getName()+":"+i);}}}
}
public class ThreadRunnable {public static void main(String[] args) {// 3、创建实现Runnable接口类的对象Number number=new Number();// 4、将对象作为Thread类的参数Thread thread=new Thread(number,"线程1");thread.start();}
}
运行结果部分截图:
第三种 实现Callable接口
1、创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
2、使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
3、使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
class cal implements Callable{private int num=0;@Overridepublic Integer call() throws Exception {for (int i = 0; i <100 ; i++) {if(i%2==0){// System.out.println(i);//输出线程名System.out.println(Thread.currentThread().getName()+":"+i);num+=i;}}return num;}
}public class CallableThread {public static void main(String[] args) throws ExecutionException, InterruptedException {// 将接口Callable实现类的对象作为FutureTask的参数FutureTask futureTask=new FutureTask(new cal());// 将FutureTask对象作为Thread的参数new Thread(futureTask, "窗口1").start();// 还可以获取Callable中call方法的返回值System.out.println("num的值:"+futureTask.get());}
}
运行结果部分截图:
第四种 通过线程池如用Executor框架
1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。要执行任务的人只需把Task描述清楚,然后提交即可。这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不用关心了。具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果就好了。Executor框架的内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象用Executor在构造器中。
/** 1、继承Thread类创建线程* 1) 定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run方法也称为线程执行体。* 2) 创建Thread子类的实例,也就是创建了线程对象* 3) 启动线程,调用线程的start()方法*/
class MyThread1 extends Thread{@Overridepublic void run() {// 重写run方法System.out.println(Thread.currentThread().getName()+"你开始拉屎?...\t"+new Date());try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"你结束拉屎?...\t"+new Date());}
}/** 2、实现Runnable接口创建线程* 1) 定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体* 2) 创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象* 3) 第三步依然是通过调用线程对象的start()方法来启动线程*/
class MyThread2 implements Runnable{@Overridepublic void run() {// 重写run方法System.out.println(Thread.currentThread().getName()+"你开始吃饭?...\t"+new Date());try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"你结束吃饭?...\t"+new Date());}
}/** 3、实现Callable接口创建线程**/
class MyThread3 implements Callable {private Integer sum=0;@Overridepublic Integer call() throws Exception {for(int i=0;i<=100;i++){Thread.sleep(100);System.out.println(Thread.currentThread().getName()+"--->"+i);sum+=i;}return sum;}
}
public class ThreadPool {public static void main(String[] args) {// 1、提供指定的线程数量的线程池ExecutorService executorService= Executors.newFixedThreadPool(10);// ThreadPoolExecutor service=(ThreadPoolExecutor) executorService;
// // 设置线程池的属性
// service.setCorePoolSize(15);executorService.execute(new MyThread1()); // 执行Runnable接口的实现类executorService.execute(new MyThread2()); // 执行Runnable接口的实现类Future submit = executorService.submit(new MyThread3());// 执行Callable接口的实现列try {System.out.println(submit.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}
// 关闭线程executorService.shutdown();}
}
运行结果部分截图:
线程创建的四种方法
线程创建的四种方法:
创建线程的常用四种方法:
第一种 继承Thread类
第二种 实现Runnable接口
第三种 实现Callable接口
第四种 通过线程池如用Executor框架
创建线程的常用四种方法:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 线程池方法创建如用Executor框架
通过继承Thread类或者Runnable接口、Callable接口都可以实现多线程,不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常而已。
采用实现Runnable、Callable接口的方式创建线程的优缺点:
优点:线程类只是实现了Runnable或者Callable接口,还可以继承其他类。这种方式下,多个线程可以共享一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。
缺点:编程稍微复杂一些,如果需要当我当前该线程,则必须使用Thread.currentThread()方法
采用继承Thread类的方法创建线程的优缺点:
优点:编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获取当前线程
缺点:因为线程类已经继承了Thread类,Java语言是单继承的,所以不能再继承其他父类。
采用线程池创建线程的优点:
1、提高响应速度----减少了创建新线程的时间
2、降低资源消耗----重复利用线程池中线程,不需要每次都创建
3、便于线程管理
第一种 继承Thread类
通过继承Thread类来创建并启动多线程的一般步骤如下
1、定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2、创建Thread子类的实例,也就是创建了线程对象
3、启动线程,即调用线程的start()方法
//1. 创建一个继承于Thread类的子类
class MyThread extends Thread{// 2. 重写Tread类的run()@Overridepublic void run() {for (int i = 0; i <100 ; i++) {if(i%2==0){// System.out.println(i);//输出线程名System.out.println(Thread.currentThread().getName()+":"+i);}}}
}
public class ThreadTest {public static void main(String[] args) {//3. 创建Tread类的子类的对象MyThread t = new MyThread();//4. 通过此对象调用start(): A :启动当前线程 B: 调用当前线程的run()方法t.start();// 第一个问题: 这是调方法 还是在主线程中执行的 我们不能直接调用run()的方式启动线程// t.run();//第二个问题:再启动一个线程 不可以让已经start()了的线程去执行 不然会报错IllegalThreadStateException// t.start();MyThread t2 = new MyThread();t2.start();// 证明线程有交互性for (int j = 0; j < 50; j++) {if(j%2!=0){System.out.println(Thread.currentThread().getName()+":"+j+"*****");}}//创建Thread类的匿名子类的方式new Thread(){@Overridepublic void run() {for (int k = 0; k <10 ; k++) {System.out.println(Thread.currentThread().getName()+": LP");}}}.start();}
}
运行部分截图:
第二种 实现Runnable接口
通过实现Runnable接口创建并启动线程一般步骤如下:
1、定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
2、创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
3、第三部依然是通过调用线程对象的start()方法来启动线程
// 1、创建一个类实现Runnable接口
class Number implements Runnable{// 2. 重写Runnable接口的run()@Overridepublic void run() {for (int i = 0; i <100 ; i++) {if(i%2==0){// System.out.println(i);//输出线程名System.out.println(Thread.currentThread().getName()+":"+i);}}}
}
public class ThreadRunnable {public static void main(String[] args) {// 3、创建实现Runnable接口类的对象Number number=new Number();// 4、将对象作为Thread类的参数Thread thread=new Thread(number,"线程1");thread.start();}
}
运行结果部分截图:
第三种 实现Callable接口
1、创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
2、使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
3、使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
class cal implements Callable{private int num=0;@Overridepublic Integer call() throws Exception {for (int i = 0; i <100 ; i++) {if(i%2==0){// System.out.println(i);//输出线程名System.out.println(Thread.currentThread().getName()+":"+i);num+=i;}}return num;}
}public class CallableThread {public static void main(String[] args) throws ExecutionException, InterruptedException {// 将接口Callable实现类的对象作为FutureTask的参数FutureTask futureTask=new FutureTask(new cal());// 将FutureTask对象作为Thread的参数new Thread(futureTask, "窗口1").start();// 还可以获取Callable中call方法的返回值System.out.println("num的值:"+futureTask.get());}
}
运行结果部分截图:
第四种 通过线程池如用Executor框架
1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。要执行任务的人只需把Task描述清楚,然后提交即可。这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不用关心了。具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果就好了。Executor框架的内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象用Executor在构造器中。
/** 1、继承Thread类创建线程* 1) 定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run方法也称为线程执行体。* 2) 创建Thread子类的实例,也就是创建了线程对象* 3) 启动线程,调用线程的start()方法*/
class MyThread1 extends Thread{@Overridepublic void run() {// 重写run方法System.out.println(Thread.currentThread().getName()+"你开始拉屎?...\t"+new Date());try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"你结束拉屎?...\t"+new Date());}
}/** 2、实现Runnable接口创建线程* 1) 定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体* 2) 创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象* 3) 第三步依然是通过调用线程对象的start()方法来启动线程*/
class MyThread2 implements Runnable{@Overridepublic void run() {// 重写run方法System.out.println(Thread.currentThread().getName()+"你开始吃饭?...\t"+new Date());try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"你结束吃饭?...\t"+new Date());}
}/** 3、实现Callable接口创建线程**/
class MyThread3 implements Callable {private Integer sum=0;@Overridepublic Integer call() throws Exception {for(int i=0;i<=100;i++){Thread.sleep(100);System.out.println(Thread.currentThread().getName()+"--->"+i);sum+=i;}return sum;}
}
public class ThreadPool {public static void main(String[] args) {// 1、提供指定的线程数量的线程池ExecutorService executorService= Executors.newFixedThreadPool(10);// ThreadPoolExecutor service=(ThreadPoolExecutor) executorService;
// // 设置线程池的属性
// service.setCorePoolSize(15);executorService.execute(new MyThread1()); // 执行Runnable接口的实现类executorService.execute(new MyThread2()); // 执行Runnable接口的实现类Future submit = executorService.submit(new MyThread3());// 执行Callable接口的实现列try {System.out.println(submit.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}
// 关闭线程executorService.shutdown();}
}
运行结果部分截图: