线程的三种创建方式详解

一、线程与进程的理解:

进程:

进程是程序在并发执行过程中操作系统分配和管理资源的基本单位。也就是资源分配与管理的最小单位,进程也可理解为一个正在运行中的应用程序。一个进程由一个或者多个线程组成。

线程:

线程是任务调度和执行的最小单位。

线程和进程之间的区别:

1.同一进程的所有线程共享该进程的地址空间。但是不同进程之间的地址空间是隔离的,也就是我们所说的独立的;

2.同一进程之间的所有线程共享该进程的资源,比如cpu,内存,io等等,进程之间的资源是独立的,非共享的。这一点跟上述的地址空间的分配有点类似。

3.每一个进程是独立的,都拥有自己 的程序执行入口,顺序执行序列。但是线程不能独立执行,必须依赖于进程,也就是正在执行的应用程序。

4.由于同一进程之间的所有线程共享资源的特性,当一个线程发生奔溃时,它的进程也会发生奔溃,但不会影响到其他进程。因为进程之间的资源分配是独立的。

二、线程的生命周期:

一个线程的生命周期分为五个状态,分别是新建状态、就绪状态、运行状态、阻塞状态、死亡状态。

新建状态:一个新的线程被创建出来,但是还没有调用start方法

就绪状态:指一个新建的线程,执行了start方法,使线程变的可运行。

运行状态:指一个线程执行了run方法。

阻塞状态:由于一个线程的时间片用完了,该线程从运行状态进入休眠状态,让出cpu资源。当时间间隔到期或者等待的事件发生了,该状态的线程切换到可运行状态。

死亡状态:一个运行状态的线程完成任务或者其他终止条件发生,该线程就切换到死亡状态。

三、java线程的三种创建方法:

  • 通过继承Thread类实现;
  • 通过实现Runnable接口实现;
  • 通过Callable接口和FutureTask 类接口创建线程;

1.通过继承Thread类实现;

首先先创建一个类,将该类继承Thread类,然后创建一个该类的实例。

继承类必须重写run()方法,该方法是新线程的入口点。它也必须调用start()方法才能执行。该方法尽管被列为一种多线程实现方式。

下面是一个实例,创建一个新的线程并执行它。

package ThreadStudy;

public class ThreadCreate1 extends Thread{
String name="";
public ThreadCreate1(String name){
	this.name=name;
	System.out.println("创建了一个新的线程"+name);
}
 public void run(){
	 System.out.println("线程"+name+":执行开始");
	 for(int i=0;i<5;i++){
		 System.out.println("线程"+name+"执行状态为"+i);
		 try {
            //暂停线程
			Thread.sleep((long) (Math.random()*20));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	 }
	 System.out.println("线程"+name+"运行结束");
 }
 public static void main(String []args){
	 System.out.println("主线程运行开始");
	 ThreadCreate1 zhangsan=new ThreadCreate1("张三");
	 ThreadCreate1 lisi=new ThreadCreate1("李四");
	 zhangsan.start();
	 lisi.start();
	 System.out.println("主线程运行结束");
 }
}

下面是运行结果:

第一次运行:

第二次运行结果:

我们从上面看出两个线程的运行结果是不一样的,我们可以看出多线程的运行是不可控,因为他们的执行顺序是由操作系统随机调度的。

2.通过实现Runnable接口来实现线程的创建:

创建一个线程,最简单的方法是创建一个实现Runnable接口的类。

为了实现Runnable,一个类只需要执行一个方法调用run(),声明如下:

public void run()

然后在通过Thread类对象调用start()方法。使线程进入到可运行状态。

下面有一个实例,可以参考一下:

package ThreadStudy;

public class RunnableCreate1 implements Runnable{
	String name=null;
	int i=0;
	public RunnableCreate1(String name){
		this.name=name;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("线程"+name+"运行开始:");
		for(int j=0;j<5;j++){
			i++;
			System.out.println("线程"+name+"运行状态:"+i);
			try {
                //暂停线程
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("线程"+name+"运行结束:");
	}
	public static void main(String []args){
		System.out.println("主线程运行开始");
		RunnableCreate1 runnable1 = new RunnableCreate1("NumberOne");
		RunnableCreate1 runnable2 = new RunnableCreate1("NumberTwo");
		new Thread(runnable1).start();
		new Thread(runnable2).start();
		System.out.println("主线程运行结束");
	}

}

输出第一次运行将结果:

输出第二次运行结果:

从上述两图可以看出运行结果还是有差异的,因为操作系统对线程的调度是随机的,不可控的。

下面有一个实例,两个线程共用一个Runnable对象:

package ThreadStudy;

public class RunnableCreate2 implements Runnable{
	int i=0;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int j=0;j<5;j++){
			System.out.println("线程"+Thread.currentThread().getName()+"运行开始");
			i++;
			System.out.println("线程"+Thread.currentThread().getName()+"运行状态"+i);
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("线程"+Thread.currentThread().getName()+"运行结束");
	}
	public static void main(String []args){
		System.out.println("主线程运行开始");
		RunnableCreate2 runnbale2 = new RunnableCreate2();
		new Thread(runnbale2,"ThreadOne").start();
		new Thread(runnbale2,"ThreadTwo").start();
		System.out.println("主线程运行结束");
	}
}

下面是输出结果:

3.通过Callable接口和Futrue创建线程

  1. 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
  2. 创建Callable实现类的实例,使用FutrueTask类来包装Callable对象,该FutrueTask对象封装了该Callable对象的call()方法的返回值。
  3. .使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
  4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

下面有一个实例:

package ThreadStudy;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableCreateTest1 implements Callable<Integer>{
	public static void main(String []args){
		CallableCreateTest1 callable = new CallableCreateTest1();
		FutureTask<Integer> futruetask = new FutureTask<>(callable);
		for(int i=0;i<20;i++){
			System.out.println(Thread.currentThread().getName()+"的循环变量i的值"+i);
			if(i==10){
				new Thread(futruetask,"新的线程").start();
			}
		}
		try {
			System.out.println("子线程的返回值"+futruetask.get());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	@Override
	//返回一个Integer类型的计算结果
	public Integer call() throws Exception {
		// TODO Auto-generated method stub
		int i=0;
		for( ;i<30;i++){
			System.out.println(Thread.currentThread().getName()+i);
		}
		return i;
	}

}

第一次运行输出运行结果:

main的循环变量i的值0
main的循环变量i的值1
main的循环变量i的值2
main的循环变量i的值3
main的循环变量i的值4
main的循环变量i的值5
main的循环变量i的值6
main的循环变量i的值7
main的循环变量i的值8
main的循环变量i的值9
main的循环变量i的值10
main的循环变量i的值11
main的循环变量i的值12
main的循环变量i的值13
main的循环变量i的值14
main的循环变量i的值15
main的循环变量i的值16
新的线程0
main的循环变量i的值17
新的线程1
main的循环变量i的值18
新的线程2
main的循环变量i的值19
新的线程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

第二次运行输出运行结果:

main的循环变量i的值14
main的循环变量i的值15
main的循环变量i的值16
main的循环变量i的值17
新的线程0
main的循环变量i的值18
新的线程1
main的循环变量i的值19
新的线程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

两次的运行结果是不同的,因为主线程也是一个线程,系统对线程的调度是随机的。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值