JAVA基础 (五) 多线程

         首先从宏观上来看一下java中多线程内容:


       本篇博客主要介绍三种启动多线程的方式,以及对比哪种方式比较好。

1.继承Thread类

       下面代码中成员变量为i,分为启用两个线程,经测试每个线程自己独占一个成员变量,即多个线程之间并不能够共享成员变量。

<span style="font-size:14px;">package com.test.process;

public class ExtendsThread extends Thread{

	private int i;
	//重写run()方法,run()方法的方法体就是线程执行体
	public void run()
	{
		for(int i=0;i<100;i++)
		{
			//当线程类继承Thread类时,直接使用this即可获取当前线程
			//Thread对象的getName()返回当前线程名字
			//因此可以直接调用getName()方法返回当前线程的名字
			System.out.println(getName()+"   "+i);
			
		}
	}
	
	public static void main(String[] args) {
		for(int i=0;i<100;i++)
		{
			//调用Thread类的currentThread()方法获取当前线程
			System.out.println(Thread.currentThread().getName()+"  "+i);
			if(i==20)
			{
				//创建并启动一个线程
				new ExtendsThread().start();
				//创建并启动第二个线程
				new ExtendsThread().start();
			}
		}
	}
}
</span>

2.实现Runnable接口

       该方式经测试两个线程共享一个成员变量,这种方式把该类作为了目标对象放到了线程中,即两个线程用的是同一个实例,因此他们成员变量是一样的,操作的一个成员变量。

<span style="font-size:14px;">package com.test.process;

public class ImplementRunnable implements Runnable{

	private int i;
	//重写run()方法,run()方法的方法体就是线程执行体
	public void run()
	{
		for( ;i<100;i++)
		{
			//当线程类实现Runnable接口时
			//如果想获得当前线程,只能使用Thread.currentThread()方法
			System.out.println(Thread.currentThread().getName()+"   "+i);
			
		}
	}
	
	public static void main(String[] args) {
		for(int i=0;i<100;i++)
		{
			//调用Thread类的currentThread()方法获取当前线程
			System.out.println(Thread.currentThread().getName()+"  "+i);
			if(i==20)
			{
				ImplementRunnable implRun=new ImplementRunnable();
				//创建并启动一个线程
				new Thread(implRun,"新线程1").start();
				//创建并启动第二个线程
				new Thread(implRun,"新线程2").start();
				new Thread(implRun,"新线程3").start();
				new Thread(implRun,"新线程4").start();
				System.out.println("123");
				
			}
		}
	}
}
</span>

3.实现Callable接口和Future接口

       Callable对runnable接口进一步封装了一下,该类可以有返回值还可以声明异常类。

<span style="font-size:14px;">package com.test.process;

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

public class CallableFuture implements Callable<Integer>{

	//实现call()方法作为线程执行体
	public Integer call()
	{
		 int i=0;
		//重写run()方法,run()方法的方法体就是线程执行体
		for( ;i<100;i++)
		{
			//当线程类实现Runnable接口时
			//如果想获得当前线程,只能使用Thread.currentThread()方法
			System.out.println(Thread.currentThread().getName()+"   "+i);
		}
		return i;
	}
	
	public static void main(String[] args) {
	
		//创建callable对象
		CallableFuture callablefuture=new CallableFuture();
		//使用FutureTask来包装callable对象
		FutureTask<Integer> futuretask=new FutureTask<Integer>(callablefuture);
		
		for(int i=0;i<100;i++)
		{
			//调用Thread类的currentThread()方法获取当前线程
			System.out.println(Thread.currentThread().getName()+"  "+i);
			if(i==20)
			{
				//创建并启动一个线程
				new Thread(futuretask,"有返回值的线程").start();
			}
		}
		
		try {
			//获取线程返回值
			System.out.println("线程返回值为="+futuretask.get());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}
</span>

三种方式对比:

       通过继承Thread类或者实现Runnable、Callable接口都可以实现多线程,不过实现Runnable与实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明异常。

       采用接口方式创建线程

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

       2.多个线程可以共享同一个target对象,非常适合多个相同线程来处理同一份资源。

       3.不过编程少复杂

      采用继承方式

      1.编程简单

      2.继承了Thread类,不能再继承其他父类

在项目中如果涉及到多线程编程,建议使用中接口的方式,这种方式可以有返回值也可以共享成员变量,给编程带来便利。

       由此,让我想到了struts1、struts2中action的创建,struts1中action是单例的所有的请求都共用一个action实例,它们都访问其成员变量故存在线程安全问题,而struts2常与spring集成,他们集成之后action交给spring管理,spring通过配置文件将bean标签的scope属性设为prototype,表示每个有请求时都为它实例化一个action,这样将会是线程安全的。

      


posted @ 2014-11-30 20:16  李龙生的博客  阅读(151)  评论(0编辑  收藏  举报