线程的3种创建方式
一、线程的概念
1.概念
1.1进程是一个应用程序的运行,运用一个应用程序只有一个进程,一个进程拥有多个线程,在java中,main方法就是一个线程,而有时我们为了提高代码的执行效率,便需要创建多个线程,去分别执行不同的代码,
1.2举个简单的例子,一个main方法需要执行10个方法,每个方法的运行时间为10秒,所以执行完毕后所需要的总时间为10*10=100秒,那么线程我们创建10个线程,每一个线程去执行一个方法,所需的总时间也就10秒,效率提高了10倍,当然在实际开发中,创建线程也是需要花费时间和资源的。
二、创建线程
1.项目结构
1.1本次项目一共5个类,不多说。直接上UML图
2.需要执行的类People
2.1本类采用了单例模式中的懒汉模式,在jvm运行创建类,并只有一个实例。这样创建,便会暴露一些多线程下的一些问题,后面看结果。
2.2代码
package com.test;
public class People {
private static People people;
private static int count;
private People(){
}
public static People getPeople(){
if(null == people){
people=new People();
}
return people;
}
public String say(){
while (count < 10) {
System.out.println(Thread.currentThread().getName()+count+"大半夜饿了,想吃泡面");
count++;
}
return "eat";
}
}
3.继承Thread
3.1在Java中,无论线程是通过哪种方式创建的,其最终还是一个Thread类。
3.2 直接上代码,ThreadOne
package com.test;
public class ThreadOne extends Thread{
public ThreadOne(String name) {
super(name);
}
@Override
public void run() {
People.getPeople().say();
}
}
4. 实现runnable
4.1实现runnable接口的好处就是,接口是可以多实现的,类的继承却只能有一个父类,所以这也是和前面那种创建方式的最大区别。
4.2代码
package com.test;
public class ThreadTwo implements Runnable{
@Override
public void run() {
People.getPeople().say();
}
}
5.实现Callable
5.1实现Callable接口,用此方法创建的线程可以接受返回值和抛出异常,这也是和前面两张创建方式的区别。
5.2代码
package com.test;
import java.util.concurrent.Callable;
public class ThreadThree implements Callable<String>{
@Override
public String call() throws Exception{
return People.getPeople().say();
}
}
6.main方法
6.1 test类,这里在创建线程时,都给线程复了一个值,线程的名称,这方便在运行结果中进行分析。
6.2代码
package com.test;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] args) {
//继承于Thread
Thread t1=new ThreadOne("one");
//实现Runnable
Thread t2=new Thread(new ThreadTwo(),"two");
//实现Callable
Callable<String> cb=new ThreadThree();
FutureTask<String> fTask=new FutureTask<String>(cb);
Thread t3=new Thread(fTask,"Three");
t1.start();
t2.start();
t3.start();
try {
System.out.println(fTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
三、总结
1.运行结果
1.1结果
one0大半夜饿了,想吃泡面
Three0大半夜饿了,想吃泡面
Three2大半夜饿了,想吃泡面
two0大半夜饿了,想吃泡面
Three3大半夜饿了,想吃泡面
Three5大半夜饿了,想吃泡面
Three6大半夜饿了,想吃泡面
Three7大半夜饿了,想吃泡面
Three8大半夜饿了,想吃泡面
Three9大半夜饿了,想吃泡面
one1大半夜饿了,想吃泡面
two4大半夜饿了,想吃泡面
eat
2.结果分析
2.1首先在结果中最后一个值为eat,说明通过Callable接口的线程类可以返回值,至于异常,我在main中将它捕获了,便先不做考虑。
2.2按照常例,如果在main中直接调用peopel类里面的eat方法,运行结果应该是0-9各执行一次,但是在多线程的情况下,会发现,0被执行了3次。这下结果是这样,下次运行这个程序,可能是1被执行了3次。结果不固定,这显然不是我们所想要的。
2.3效率我们通过创建多个线程提高了,而结果我们又应该怎样去得到保障了。这就是我们后面的文章中会讲到,锁机制。一环扣一环,一口不能吃个大胖子,我们一步一步的去解决问题。
3.结语
我们千万不要把线程想得太复杂了,以前老是听人家说什么多线程,nio,很牛的样子,感觉很复杂,其实复杂的东西就是很多简单的东西组合而来,一步一步来,很容易理解,如果文章中有什么地方不对,或者觉得不够理解,还望指出,一定改之。