java-创建线程的方式

线程是进程组成部分,一个进程可以有多个线程,一个线程必须有一个父进程。线程的优点:多个线程共享内存,并且可以高并发,多个线程共享一个进程的虚拟空间。线程有这么多好处,那么他的创建方法有三种方式:

方式1:通过继承Thread类来创建线程。

具体步骤:

a.定义Thread类的子类,并重写该类的run()方法。run()方法的方法体是线程需要完成的任务,称为线程执行体

b.创建Thread子类的实例。

c.调用线程对象的start()方法来开启线程。

public class FirstThread extends Thread {

	/*创建的线程不能共用这个实例变量,每次新创建的线程对象的实例变量是不同。*/
	private Integer i=0 ;
	
	@Override
	public void run(){
		for(;i<10;i++){
			System.out.println(this.getName()+"\t"+i);
		}
	}
	
}

通过继承Thread类的实例,可以通过this来得到当前线程信息。

具体调用:

public class FirstThreadTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		for(int i = 0 ;i < 10 ;i ++){
			System.out.println(Thread.currentThread().getName()+"\t"+i);
			if(i==5){
				new FirstThread().start();
				new FirstThread().start();
			}
		}
	}
}

其中,运行的线程有个:主线程 - main方法-主线程执行体 , 新创建的两个线程 new FirstThread() - run方法-线程执行体。

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

方式2:通过实现Runnable接口实现类来创建线程。

步骤:

a.定义Runnable接口的实现类,必须重写接口中的run()方法,作为线程执行体。

b.创建Runnable接口实现类的实例,并以此作为Thread的target来创建Thread对象,该对象才是真正的Thread对象。

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

public class SecondThread implements Runnable {

	private Integer i;
	@Override
	public void run() {
		// TODO Auto-generated method stub

		for(i = 0 ;i <10 ; i++){
			System.out.println(Thread.currentThread().getName()+"\t"+i);
		}
	}

}

通过Runnable接口实现类创建的对象,必须通过Thread.currentThread()方法来得到当前线程对象。

public class SecondThreadTest {

	public static void main(String[] args) {

		SecondThread st = new SecondThread();
		for(int i = 0;i<10;i++){
			if(i==5){
				new Thread(st,"线程1").start();
				new Thread(st,"线程2").start();
			}
			System.out.println(Thread.currentThread().getName()+"\t"+i);
		}
	}

}

通过运行该程序,可以发现两个子线程的i值是连续的,说明该方式创建的线程可以共享线程类的实例变量。原因是因为:实现类的实例只是线程的target,而多个线程可以共享同一个target。

方式3:使用Callable和Future创建线程。(JAVA5之后的版本可以,JAVA8之后的版本可以通过Lambda表达式来简化步骤

使用原因:通过实现Runnable接口创建多线程时,Thread类的作用就是将run()方法包装成线程执行体。Callable接口类似于Runnable接口的增强版,提供了一个call()方法作为线程执行体,但是run()方法优势在于可以有返回值和抛出异常。但是由于Callable实现类的实例有返回值,是新增接口,不能直接作为Thread类的target,所以通过Future接口提供类一个FutureTask实现类的实例来作为Thread类的target。

步骤:

a.创建Callable接口的实现类,并实现Call()方法,作为线程执行体,且有返回值,再创建Callable实现类的实例。

b.使用futureTask类来包装Callable对象,直接封装了Callable对象的call()方法的返回值。

c.使用FutureTask对象作为Thread对象的target创建并启动线程。

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

public class ThirdThread implements Callable<Integer> {

	@Override
	public Integer call() throws Exception {
		int i = 0 ;
		for(;i<10;i++){
			System.out.println(Thread.currentThread().getName()+"\t"+i+"子线程执行体");
		}
		return i;
	}

}

调用:

public class ThirdThreadTest {
	
	public static void main(String[] args){
		FutureTask<Integer> ft = new FutureTask<Integer>(new ThirdThread());
		for(int i = 0 ;i <10 ;i++){
			System.out.println(Thread.currentThread().getName()+"的循环变量i是"+i);
			if(i==5){
				new Thread(ft,"有返回值的线程").start();
			}
		}
		
		try {
			System.out.println("子线程的返回值:"+ft.get());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}

以上两种类型三种方式的优缺点:

继承Thread类的方式:

优点:步骤简单,如需访问当前线程,可以直接用this来调用。

缺点:无法继承其他类。

实现Runnable接口或者Callable/Future接口的方式:

优点:多个线程可以共享一个target对象,所以适合用于多个线程处理同一个资源的情况,从而将CPU、数据、代码分开,形成清晰的模型。

缺点:编程复杂,若想访问当前线程只能通过Thread.currentThread()方法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值