线程
1.线程介绍
(1)什么是进程?线程?程序?
-
**程序(program):**是为了完成特定的语言编写的一组指令的集合。即是一段特定的代码。
-
**进程(process):**程序的一次执行过程,或正在运行的一个程序。
-
**线程:(thread:**进程可进一步化为线程,是一个程序内部的一条执行路径。
(2)并行和并发的理解
- 单核CPU和多核CPU的理解
单核CPU,其实是一种假的线程,因为在一个时间单元内,也只能执行一个线程的内务。例如: 有很多车道,但是只有一个工作人员在收费,只有收费的才能通过,那么CPU就好比收费人员。如果有某个人不想交钱,那么收费人员可以把它“挂起”(晾着他,等 他想通了,准备好了钱,再去收费)。但是因为CPU时间单元特别短,因为感觉不出来。
如果是多核的话,才能更好的发挥多线程的效率。(现在服务器都是多核的)
java的应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,
异常处理线程,如果发生异常,会影响主线程。
- 并行与并发
并行:多个CPU同时执行多个任务。比如多个人同时再做多个不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀,多个人做同一个事。
(3)线程的生命周期
线程的声明周期通常需要经历以下五种状态:
新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU并临时中止自己的执行,进入阻塞状态
死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
- 线程的声明周期:
2.线程的创建四种方式:
2.1.使用类继承Thread
1、创建一个类继承Thread,并重写Thread类的run方法。
2、实例化创建的类并调用Thread类的start方法
Thread线程start方法一个对象只能调用一次,要想创建多个线程就需要创建多个对象。并调用start方法。
- 使用匿名子类方式:
new Thread(){
@Override
public void run() {
//业务代码
}
}.start();
2.2实现Runnble接口:
1、创建一个实现了Runnable接口的类
2、实现类去实现Runnable中的抽象方法:run()
3、创建实现类的对象
4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5、遹过Thread类的对象调用start();
**补充:**在实现Runnable接口创建多线和的方式中,我们可以考虑使用this充当同步监视器。
在继承Thread类创建多线程的方式中,镇用this在当同步监视器,考虑使用当前类充当同步监视器
- 使用Runnble接口实现多线程:
class RuThread implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public static void main(String[] args) {
RuThread ruThread = new RuThread();
Thread t1 = new Thread(ruThread);
t1.setName("线程一");
t1.start();
Thread t2 = new Thread(ruThread);
t2.start();
t2.setName("线程二");
Thread.currentThread().setName("主线程");
System.out.println(Thread.currentThread().getName());
}
开发中:优先选择;实现Runnable接口的方式原因:
1、实现的方式没有类的单继承性的局限性。
2.实现的方式更适合来处理多个线程有共享数据的情况。(继承数据要用static修饰,而接口方式使用同一个类,参数可以共享。)
联系:Thread类本身实现了Runnble接口。
相同点:两种方式都重写run方法。将线程要执行的逻辑声明在run()中。
JDK5.0线程新特性
2.3 实现Callable接口:
实现步骤:
1.创建1个callable接口的实现类
2.实现call方法,将此线程需要执行的操作声明在call( )中
使用:
3.创建callable接口实现类
4.将此callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask对象。
5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调start启动线程。
6.获取CalLable 中call方法返回值。
- 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
- 优点:
- 1.call方法可以有返回值。
- 2.call方法可抛出异常,被外面的操作捕获,获取异常的信息。
- 3.call是支持泛型的。
使用案例(实现偶数求和)
//1.创建1个callable接口的实现类
class Call implements Callable<Integer> {
//2.实现call方法,将此线程需要执行的操作声明在calL( )中
@Override
public Integer call() throws Exception {
//public Object call() throws Exception {
int sum=0;
for (int i = 0; i <100 ; i++) {
if (i%2==0){
sum+=i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.创建callable接口实现类
Call call = new Call();
//4.将此callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask对象
// FutureTask futureTask = new FutureTask(call);
FutureTask<Integer> futureTask = new FutureTask<Integer>(call);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调start启动线程
new Thread(futureTask).start();
try {
//6.获取CalLable 中call方法返回值
Integer o = futureTask.get();
System.out.println("总和:"+o);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
2.4线程池
**思路:**提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:
1.提高响应速度(减少了创建新线程的时间)。
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)。
3. 便于线程管理:
-
corePoolsize:核心心的大小
-
maximumPoolSize:最大线程数
-
keepAliveTime:线程没有任务时最多保持多长时间后会终止
使用线程
class NumBer implements Runnable{
Object obj