线程是指一个进程内部的子任务,是任务的具体执行的逻辑。并且一个进程至少有一个线程(这个唯一的线程是主线程)
注:进程启动时创建的堆区(Heap)和云空间(MetSpace)是同一个进程内的多个线程共享的
每个线程都有自己的线程:PC、虚拟机栈、本地方法栈。
JVM必有得五大线程:
- 主线程
- 清除reference线程
- 调用对象finalize方法的线程
- 分发处理给JVM信号的线程
- 添加事件监听器
创建线程的方式:
Thread t = new Thread();
注:这句代码是创建线程的最根本的方式,其中传入的参数可以是一个:String类型的线程名字,Runnable类型的实现类;也可以是两个都传。
根据传入的参数可以将创建方式分为:
方式一、继承Thread类
Thread t = new Thread(){ }
t这个线程被调用时会自动调用重写的run()方法。
//自已创建的线程1
Thread t1 = new Thread() {
//在这里面写线程内部要进行的操作
@Override
public void run() {
System.out.println(i);
}
};
方式二:实现Runnable接口
Thread t = new Thread(new Runnable( ){ })
注:若采用自己定义A类的实现Runnable接口,在使用这个线程时,-->
在主线程中:首先创建A类对象(创建出来后并不是创建出了线程)
然后通过 Thread t = new Thread( a ) -->这个过程才是真正创建线程的步骤。
eg:
public class EmailSend extends TasK implements Runnable{
@Override
public void run() {
//获取当前的线程
Thread currentThread = Thread.currentThread();
//获取当前线程的名字
String name = currentThread.getName();
//具体操作
System.out.println(name + "发送邮件");
}
}
public static void main(String[] args) {
//创建自己写的这个类的对象后,并没有创建一个新的线程
EmailSend emailSend = new EmailSend();
//通过这种方式才代表创建了一个新的线程
Thread t1 = new Thread(emailSend, "线程1");
Thread t2 = new Thread(emailSend, "线程2");
Thread t3 = new Thread(emailSend, "线程3");
t1.start();
t2.start();
t3.start();
}
方式三:实现Callable接口
(此方式创建能够获得任务具体实现获得的返回结果)
同样,若自己定义A类实现这个接口,也是在内部写具体的任务逻辑,而在使用时要注意:
在main中,创建A类对像后( A a = new A() ),由于Thread类中的构造函数中,只能传入Runnable类型的,所以要用一个中间类进行转换,将Callable类型转成Runnable,以便能够放入Thread创建线程。
而这个中间类就是 FutureTask<T> f = new Future<T>( a );
原因:FutureTask是RunnableFuture的实现类,RunnableFuture又继承自Runnable,所以FutureTask间接继承自Runnable,所以能够传入到具体的线程创建操作中。
eg:
MyCallableImpl myCallableImpl1 = new MyCallableImpl(1, 100);
MyCallableImpl myCallableImpl2 = new MyCallableImpl(101, 200);
MyCallableImpl myCallableImpl3 = new MyCallableImpl(201, 300);
由于Thread对象中不能传入Callable类型,
所以要通过中间类FutureTask进行转换,,而FutureTask是RunnableFuture的实现类,
RunnableFuture又继承自Runnable,所以FutureTask间接继承自Runnable类,所以能够传入到Thread中
FutureTask<Integer> futureTask1 = new FutureTask<Integer>(myCallableImpl1);
FutureTask<Integer> futureTask2 = new FutureTask<Integer>(myCallableImpl2);
Thread t1 = new Thread(futureTask1,"线程1");
Thread t2 = new Thread(futureTask2,"线程2");
class MyCallableImpl implements Callable<Integer>{
private int begin,end;
public MyCallableImpl(int begin,int end){
this.begin = begin;
this.end = end;
}
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "线程的call被调用了");
int sum = 0;
for(int i = begin;i<=end;i++) {
sum = sum +i;
}
return sum;
}
}
方式四:创建线程池:
//利用线程池:
//1、创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
//2、提交Callable接口封装的具体实现
Future<Integer> f1 = executorService.submit(myCallableImpl1);
Future<Integer> f2 = executorService.submit(myCallableImpl2);
Future<Integer> f3 = executorService.submit(myCallableImpl3);
//3、分别获取各线程计算的结果
Integer i1 = f1.get();
Integer i2 = f2.get();
Integer i3 = f3.get();
//4、汇总结果
int ret = i1 + i2 + i3;
System.out.println(ret);
//5、关闭线程池
executorService.shutdownNow();
上面的案例中,创建的是固定大小的线程池,且最多只能有三个线程。若有一个线程需要执行,系统会将任务提交给线程池。将线程池中的空闲线程分配给他;若没有空闲线程,就会进入等待状态。