首先从宏观上来看一下java中多线程内容:
本篇博客主要介绍三种启动多线程的方式,以及对比哪种方式比较好。
1.继承Thread类
下面代码中成员变量为i,分为启用两个线程,经测试每个线程自己独占一个成员变量,即多个线程之间并不能够共享成员变量。
<span >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 >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 >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,这样将会是线程安全的。