java线程与多线程_Java实现多线程的几种方法和区别

说起java多线程编程,大家都不陌生,下面我就总结下java里实现多线程的集中方法:

1、继承Thread类

方法:继承Thread类,重写run()方法,实例化线程类,调用start()方法启动线程。

代码如下:

通过继承Thread类创建线程类

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

packagecom.goomoon.JavaDemo;

/**

* 通过继承 Thread 并重写 run()方法 来创建线程类

* @author kevin

*/

publicclassThreadTestextendsThread{

/**

* 重写run() 方法 ,run()方法的方法体就是线程执行体

*/

@Override

publicvoidrun(){

for(inti=0;i<100;i++){

/**

* 使用getName() 获取当前线程的名称

*/

System.out.println(getName()+":"+i);

}

}

publicstaticvoidmain(String[]args){

for(inti=0;i<100;i++){

//调用Thread类的currentThread()方法获取当前线程

System.out.println(Thread.currentThread().getName()+":"+i);

if(i==20){

//创建并启动第1个线程

newThreadTest().start();

//创建并启动第2个线程

newThreadTest().start();

}

}

}

}

这里有一个小知识点,我觉得挺有意思,那就是:

第一种方法:

Java

1

2

ThreadTestinstance=newThreadTest();

instance.start();

第二种方式:

Java

1

2

ThreadTestinstance=newThreadTest();

instance.run();

这两种方式有什么区别呢??

第一种方式,是另外起一个线程,至于这个线程具体的执行时间需要靠JVM内部线程调度器进行调度来决定何时进行轮询执行,而第二种方式呢,就和一个普通Java类一样,并没有新起一个线程,而是作为一个普通的类对象执行方法,并且是立即执行

2、实现Runnable接口

方法:实现Runnable接口并重写Run()方法,创建Runnable实例,并以此实例作为Thread类的target参数创建Thread对象,调用Thread对象的start()启动线程

代码如下:

实现Runnable接口

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

packagecom.goomoon.JavaDemo;

//通过实现Runnable接口来创建线程类

publicclassRunnableTestimplementsRunnable{

/**

* run()方法同样是线程执行体

*/

@Override

publicvoidrun(){

for(inti=0;i<100;i++){

/**

* 实现Runnable接口的方法体里,只能通过Thread.currentThread()来获取当前的线程

*/

System.out.println(Thread.currentThread().getName()+":"+i);

}

}

publicstaticvoidmain(String[]args){

//实例化Runnable类,创建一个任务

Runnableinstance=newRunnableTest();

//用Thread类来创建目标线程,并start()启动线程

newThread(instance).start();

//当前主线程继续执行

for(inti=0;i<100;i++){

System.out.println(Thread.currentThread().getName()+":"+i);

}

}

}

其实 这两种实现方式,是java里最典型的两种实现方式。除了这两种方法呢,Java里还提供了第三种方式:Callable接口,看起来很像Runnable接口的增强版,不过其提供了一个call()方法作为线程执行体。但是call()比run()方法更加强大,因为最重要的点:call()可以有返回值。下面就仔细看下怎么用Callable接口来创建线程类。

3、使用Callable接口和Future来创建线程

Callable 不是 Runnable接口的子接口,不能直接作为Thread类的target参数。所以呢??看Java API可以了解,Java里有个FutureTask类实现了Future接口,更重要的是FutureTask类还实现了Runnable接口,这样就可以作为Thread类的target被执行了。

方法:实现Callable接口,并实现call()方法,并创建Callable实例,然后使用FutureTask对象来包装Callable实例,使用FutureTask对象作为Thread的target来创建并启动线程

代码如下:

使用Callable接口和Future来创建线程

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

packagecom.goomoon.JavaDemo;

importjava.util.concurrent.Callable;

importjava.util.concurrent.ExecutionException;

importjava.util.concurrent.FutureTask;

//使用Callable接口和Future来创建线程

publicclassThreadFuture{

//抛出异常

publicstaticvoidmain(String[]args)throwsInterruptedException,ExecutionException{

//创建FutureTask对象,包装 Callable接口实例

FutureTasktask=newFutureTask((Callable)()->{

intsum=0;

for(inti=0;i<100;i++){

System.out.println(Thread.currentThread().getName()+":"+i);

sum+=i;

}

//注意看这里有返回值

returnsum;

});

//使用task作为 Thread类的target 来创建一个线程

Threadinstance=newThread(task);

//启动线程

instance.start();

//sleep一段时间,让上面的线程执行完毕

Thread.sleep(1000);

//这里可以调用task.get() 获取上面的那个线程的返回值

System.out.println("线程返回值:"+task.get());

}

}

如上面代码所示,在后面可以通过

使用Callable接口和Future来创建线程

Java

1

task.get()

来获取线程的返回值,这也是线程之间通信的一种方式呢。上面有一段代码:

Java

1

2

3

4

5

6

7

8

9

FutureTasktask=newFutureTask((Callable)()->{

intsum=0;

for(inti=0;i<100;i++){

System.out.println(Thread.currentThread().getName()+":"+i);

sum+=i;

}

//注意看这里有返回值

returnsum;

});

咋一看,好像是在FutureTask的构造函数里声明了一个匿名内部类,仔细看呢又不太像,这一种写法其实是Java8里的一种新特性:Lambda表达式,使用Lambda表达式直接创建函数式接口的实例。其实和下面一种写法一致:

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

Callablecall=newCallable(){

@Override

publicIntegercall()throwsException{

intsum=0;

for(inti=0;i<100;i++){

System.out.println(Thread.currentThread().getName()+":"+i);

sum+=i;

}

//注意看这里有返回值

returnsum;

}

};

FutureTasktask=newFutureTask(call);

总结:根据上面三种方式呢都可以实现多线程,不过Runnable接口和Callable接口的方式基本相同(实现Runnable接口和Callable接口仅仅是创建了一个任务,但是仍然需要Thread类来创建线程并启动),他俩唯一区别只是Callable接口实现方法有返回值,并且也可以声明抛出异常而已,因此②和③可以归为一类,下面主要讨论和①中方法的区别和优缺点:

1、方法②和③,不仅实现了Runnable接口和Callable接口,还可以继承其他类,可拓展性比较好

2、多个线程共享一个target,所以更适合多线程共享统一资源。

3、第①中方法,实现起来比较简单,但是不太适合多个线程共享一个资源的情况。

4、还有一个小小的区别,就是在线程执行体里调用当前线程的方式不一样,在Thread子类的run()方法里可以直接使用this获取当前线程,而在Runnable和Callable的run()实现里,只能通过Thread.currentThread()来获取。

鉴于上面的分析,因此一般推荐采用Runnable接口或者Callable接口来实现多线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值