首先从宏观上来看一下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,这样将会是线程安全的。