Java多线程——创建线程的四种方式
创建线程方式一:继承Thread类。
步骤:
1,定义一个类继承Thread类。
2,覆盖Thread类中的run方法。
3,直接创建Thread的子类对象创建线程。
4,调用start方法开启线程并调用线程的任务run方法执行。
可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)
主线程的名字就是main。
例:
class Demo extends Thread { private String name; Demo(String name) { super(name); //父类构造函数,改线程的名称 //this.name = name; } //***run方法中定义就是线程要运行的任务代码。*** public void run() { for(int x=0; x<10; x++) { //for(int y=-9999999; y<999999999; y++){} System.out.println(name+"....x="+x+".....name="+Thread.currentThread().getName()); } } } class ThreadDemo2 { public static void main(String[] args) { Demo d1 = new Demo("旺财"); Demo d2 = new Demo("xiaoqiang"); d1.start();//开启线程,调用run方法。 d2.start(); System.out.println("over...."+Thread.currentThread().getName()); } }
创建线程方式二 :当该类有自己父类的时候,通过实现Runnable接口,覆盖run方法。(*常用*)
步骤:
1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
所以要在线程对象创建时就必须明确要运行的任务。
思想:将线程的任务通过Runnable接口封装成了对象。
4,调用线程对象的start方法开启线程。
实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。
2,避免了java单继承的局限性。
例:
class Demo implements Runnable//extends Fu //准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。 //通过接口的形式完成。 { public void run() { show(); } public void show() { for(int x=0; x<20; x++) { System.out.println(Thread.currentThread().getName()+"....."+x); } } } class ThreadDemo { public static void main(String[] args) { Demo d = new Demo(); Thread t1 = new Thread(d); Thread t2 = new Thread(d); t1.start(); t2.start(); } }
创建线程方式三 :实现Callable接口
与使用Runnable相比, Callable功能更强大些
1 相比run()方法,可以有返回值
2 方法可以抛出异常
3 支持泛型的返回值
4 需要借助FutureTask类,比如获取返回结果
Future接口
1 可以对具体Runnable、Callable任务的执行结果进行取消、查询是
否完成、获取结果等。
2 FutrueTask是Futrue接口的唯一的实现类
3 FutureTask 同时实现了Runnable, Future接口。它既可以作为 Runnable被线程执行,又可以作为Future得到Callable的返回值
1 //1.创建一个实现Callable的实现类 2 class Stu implements Callable { 3 //2.实现call方法,将此线程需要执行的操作生命call()中 4 @Override 5 public Object call() throws Exception { 6 int sum=0; 7 for (int i = 1; i <=100; i++) { 8 if(i % 2 == 0){ 9 System.out.println(i); 10 sum += i; 11 } 12 } 13 return sum; 14 } 15 } 16 17 public class Bank { 18 public static void main(String[] args) { 19 //3.创建Callable接口实现类的对象 20 Stu stu = new Stu(); 21 //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象 22 FutureTask futureTask = new FutureTask(stu); 23 //5.FutureTask的对象作为参数传递到Thread类的构造器中创建Thread,并调用start() 24 new Thread(futureTask).start(); 25 try { 26 Object sum = futureTask.get(); 27 System.out.println("总和为"+sum); 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } catch (ExecutionException e) { 31 e.printStackTrace(); 32 } 33 34 } 35 }
偶数是2
偶数是4
偶数是6
偶数是8
偶数是10
偶数是12
偶数是14
偶数是16
偶数是18
偶数是20
偶数是22
偶数是24
偶数是26
偶数是28
偶数是30
偶数是32
偶数是34
偶数是36
偶数是38
偶数是40
偶数是42
偶数是44
偶数是46
偶数是48
偶数是50
偶数是52
偶数是54
偶数是56
偶数是58
偶数是60
偶数是62
偶数是64
偶数是66
偶数是68
偶数是70
偶数是72
偶数是74
偶数是76
偶数是78
偶数是80
偶数是82
偶数是84
偶数是86
偶数是88
偶数是90
偶数是92
偶数是94
偶数是96
偶数是98
偶数是100
总和为2550
创建线程方式四 :使用线程池
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程, 对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完 放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交 通工具。
好处:
1提高响应速度(减少了创建新线程的时间)
2降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
1 //创建并使用多线程的第四种方法:使用线程池 2 class MyThread implements Runnable { 3 4 @Override 5 public void run() { 6 for (int i = 1; i <= 100; i++) { 7 if(i % 2 ==0){ 8 System.out.println(Thread.currentThread().getName() + ":" + i); 9 } 10 } 11 } 12 13 } 14 15 public class ThreadPool { 16 public static void main(String[] args) { 17 // 1.提供指定线程的数量 18 ExecutorService service = Executors.newFixedThreadPool(10); 19 //设置线程的属性 20 ThreadPoolExecutor service1= (ThreadPoolExecutor) service; 21 //service1.setMaximumPoolSize(15); 22 //service1.setCorePoolSize();*/ 23 // 2.将Runnable实现类的对象作为形参传递给ExecutorService的submit()方法中,开启线程 24 // 并执行相关的run() 25 service.execute(new MyThread());//适用于Runnable 26 //service.submit();适用于Callable 27 28 // 3.结束线程的使用 29 service.shutdown(); 30 31 } 32 }