java--线程的构建(来源疯狂java讲义)

首先在这里先介绍一下线程和进程的区别:

进程是一个程序一次运行的状态,进程是系统进行资源分配和调度的一个独立的单位。

进程包括三个特征:

独立性:进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己的私有地址空间,在没有经过进程本身允许的情况下,一个用户的进程不可以直接访问其他进程的地址空间。

动态性:进程和程序的区别在于,程序只是一个静态的指令集合。而进程是一个正在系统中活动的指令集合。在进程中加入了时间概念。进程具有自己的生命周期和各种不同的状态。这些概念在程序中都是不具备的。

并发性:多个进程可以在单个处理器上并发执行。多个进程之间不会互相影响。

并发性和并行性:这是两个不同的概念,并行性是指在同一时刻,有多条指令在多个处理器上同时执行,并发是指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。

其次,我们介绍一下线程的创建和启动:

线程的创建有:继承Thread类、实现Runnable接口、使用CallableFuture创建线程

1、继承Thread类:

它在java.lang.Thread   

原形为:

Public class Thread extends Object implements Runnable{}

它实现了Runnable 接口。

通过继承Thread类创建线程类步骤入下:

(1)、定义Thread类的子类,并重写run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。

(2)创建Thread子类的实例,即创建线程对象。

(3)调用线程对象的start()方法来启动该线程。

Start()方法,就是提醒一下该线程该运转了,然后立刻读取start()下面的代码。

下面列举一个通过继承Thread类来创建线程类。

 1 Public class FistThread extends Thread{
 2 
 3 Private int i;
 4 
 5 //重写run 方法,run方法的方法体就是线程的执行体
 6 
 7 Public void run(){
 8 
 9 For ( ; i<100 ; i++){
10 
11 //当线程类继承Thread类时,直接使用this即可以获取当前线程
12 
13 //Thread对象的getName()返回当先线程的名字
14 
15 //因此可以直接调用getName()方法返回当前线程的名字
16 
17 System.out.println ( getName() +” ”+i);
18 
19 }
20 
21 }
22 
23 Public static void main (String [] args){
24 
25 For(int i=0; i<100 ;i++){
26 
27 //调用Thread的currentThread()方法获取当前线程
28 
29 System.out.println(Thread.currentThread().getName()+ “ ”+i);
30 
31 If(i==20){
32 
33 //创建启动第一个线程
34 
35 new FirstThread().start();
36 
37 //创建启动第二个线程
38 
39 new FirstThread().start();
40 
41 } 
42 
43 }
44 
45 }
46 
47 }

使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。

就上面的例子,相当于创建了两个线程,每个线程都能够运行100次。

2、实现Runnable 接口创建线程类:

实现Runnable接口来创建并启动多线程步骤如下:

(1)、定义Runnable接口的实现类,并重写该类接口的run方法,该run方法的方法体同样是该线程的执行体。

(2)、创建Runnable实现类的实例,并以此例作为Threadtarget来创建Thread对象,该Thread对象才是真正的线程对象。

(3)、调用线程对象start()方法来启动该线程。

下面列举一个通过实现Runnable接口创建线程类。

 1 Public class SecondThread implements Runnable{
 2 
 3 Private int i;
 4 
 5 Public void run (){
 6 
 7 For( ; i<100;i++){
 8 
 9 //当线程类实现Runnable接口时
10 
11 //如果想获得当前线程,只能用Thread.currentThread()方法
12 
13 System.out.println(Thread.currentThread().getName() +” ”+i);
14 
15 }
16 
17 }
18 
19 Public static void main(String []  args){
20 
21 For (int i=0;i<100;i++){
22 
23 System.out.println(Thread.currentThread().getName()+” ”+i);
24 
25 If(i==20){
26 
27 SecondThread st =new SecondThread();
28 
29 //通过new Thread(target,name)方法创建新线程
30 
31 New Thread (st,”新线程1”).start();
32 
33 New Thread(st,”新线程2”).start();
34 
35 }
36 
37 }
38 
39 }
40 
41 }

运行结果可以看出去两个线程i变量是连续的,也就是采用Runnable 接口的方式创建多个线程可以共享线程类的实例属性。

3、使用Callable 和 Future 创建线程

首先为什么出现这种方法。先看看前两个方法创建线程的缺点。

通过实现 Runnable 接口创建多线程时,Thread 类的作用就是把run方法包装成线程执行体,那么是否可以直接把任何方法包装成线程执行体?Java 目前是不行的。

Java 5 开始,Java 提供了Callable 接口,该接口是Runnable 的增强版,Callable 接口提供了一个call方法,该方法可以有返回值 也可以声明抛出异常。

注意:Callable 接口有泛型限制,Callable 接口里的泛型参数类型与call方法返回值类型相同。

创建并启动有返回值得线程的步骤如下:

(1)、创建Callable 接口的实现类,并实现call()方法,该call方法作为线程执行体,且该call方法有返回值。

(2)创建Callable实现类的实例,并使用FutureTask类来包装Callable对象,该FutureTask 对象封装了该Callable对象的call方法的返回值。

(3)使用FutureTask 对象作为Thread 对象的target  来创建并启动线程。

(4)调用FutureTask 对象的get方法来获得子线程执行结束之后的返回值。

下面程序通过实现Callable 接口来实现线程类,并启动该线程:

 1 import java.util.concurrent.Callable;
 2 
 3 import java.util.concurrent.FutureTask;
 4 
 5 public class ThirdThread implements Callable<Integer>
 6 
 7 {
 8 
 9 //实现call()方法,作为线程的执行体
10 
11 public Integer call(){
12 
13 int i=0;
14 
15 for( ;i<20;i++){
16 
17 System.out.println(Thread.currentThread().getName()+"的循环变量 i的值为:"+i);
18 
19 }
20 
21 return i;
22 
23 }
24 
25 public static void main(String[] args){
26 
27 //创建Callable 对象
28 
29 ThirdThread rt=new ThirdThread();
30 
31 //使用FutureTask来包装Callable对象
32 
33 FutureTask<Integer> task= new FutureTask<Integer>(rt);
34 
35 for(int i=0 ;i<20;i++){
36 
37 System.out.println(Thread.currentThread().getName()+"的循环变量为:"+i);
38 
39 if(i==5){
40 
41 //开始线程
42 
43 new Thread(task,"有返回值的线程").start();
44 
45 }
46 
47 }try
48 
49 {
50 
51 System.out.println("子线程的返回值为:"+task.get());
52 
53 }
54 
55 catch (Exception e)
56 
57 {
58 
59 e.printStackTrace();
60 
61 }
62 
63 }
64 
65  
66 
67 }
View Code

记住需要import java.util.concurrent.Callable;  import java.util.concurrent.FutureTask;

程序最后调用FutureTask 对象的get方法来返回call方法的返回值------该方法将导致主线程被阻塞,直到call方法结束并返回为止。

如果代码为下面这样:

 1 import java.util.concurrent.Callable;
 2 
 3 import java.util.concurrent.FutureTask;
 4 
 5 public class ThirdThread implements Callable<Integer>
 6 
 7 {
 8 
 9 //实现call()方法,作为线程的执行体
10 
11 public Integer call(){
12 
13 int i=0;
14 
15 for( ;i<20;i++){
16 
17 System.out.println(Thread.currentThread().getName()+"的循环变量 i的值为:"+i);
18 
19 }
20 
21 return i;
22 
23 }
24 
25 public static void main(String[] args){
26 
27 //创建Callable 对象
28 
29 ThirdThread rt=new ThirdThread();
30 
31 //使用FutureTask来包装Callable对象
32 
33 FutureTask<Integer> task= new FutureTask<Integer>(rt);
34 
35 for(int i=0 ;i<20;i++){
36 
37 System.out.println(Thread.currentThread().getName()+"的循环变量为:"+i);
38 
39 if(i==5){
40 
41 //开始线程
42 
43 new Thread(task,"有返回值的线程").start();
44 
45 try{
46 
47 System.out.println("子线程的返回值为:"+task.get());
48 
49 }
50 
51 catch (Exception e)
52 
53 {
54 
55 e.printStackTrace();
56 
57 }
58 
59 }
60 
61  
62 
63 }
64 
65 }
66 
67  
68 
69 }

那么输出结果为:

main的循环变量为:0

main的循环变量为:1

main的循环变量为:2

main的循环变量为:3

main的循环变量为:4

main的循环变量为:5

有返回值的线程的循环变量 i的值为:0

有返回值的线程的循环变量 i的值为:1

有返回值的线程的循环变量 i的值为:2

有返回值的线程的循环变量 i的值为:3

有返回值的线程的循环变量 i的值为:4

有返回值的线程的循环变量 i的值为:5

有返回值的线程的循环变量 i的值为:6

有返回值的线程的循环变量 i的值为:7

有返回值的线程的循环变量 i的值为:8

有返回值的线程的循环变量 i的值为:9

有返回值的线程的循环变量 i的值为:10

有返回值的线程的循环变量 i的值为:11

有返回值的线程的循环变量 i的值为:12

有返回值的线程的循环变量 i的值为:13

有返回值的线程的循环变量 i的值为:14

有返回值的线程的循环变量 i的值为:15

有返回值的线程的循环变量 i的值为:16

有返回值的线程的循环变量 i的值为:17

有返回值的线程的循环变量 i的值为:18

有返回值的线程的循环变量 i的值为:19

子线程的返回值为:20

main的循环变量为:6

main的循环变量为:7

main的循环变量为:8

main的循环变量为:9

main的循环变量为:10

main的循环变量为:11

main的循环变量为:12

main的循环变量为:13

main的循环变量为:14

main的循环变量为:15

main的循环变量为:16

main的循环变量为:17

main的循环变量为:18

main的循环变量为:19

也就验证了上面的情况  get方法会使主线程阻断。直到call方法结束后。才调用主线程。

创建线程的三种方式对比:

采用实现Runnable Callable 接口的方式创建多线程——

线程类只是实现了Runnable 接口或Callable 接口 ,还可以继承其他类。

在这种方式下,多线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU 、代码、和数据分开。

劣势是:编程稍稍复杂,如果需要访问当前线程,则必须使用Thread.currentThred()方法。

采用继承Thread类的方式创建多线程————

优势:编写简单,如果需要访问当前线程,则无须使用Thread.currentThread()方法。直接使用this 即可获得当前线程。

劣势:因为线程只能继承一个父类。所以它继承了Thread 不能在继承其他的父类了。

原文地址:https://www.cnblogs.com/teng-IT/p/4439084.html