1.线程、进程、程序
在了解线程之前先了解一下线程、进程、程序它们之间的关系。
程序:指令和数据的有序集合,其本身并没有任何运行含义。可以理解为程序就是一段代码(个人理解,如有错欢迎指正)。
进程:计算机中正在运行的程序,一个启动的应用至少有一个进程正在运行,一个进程就好比一个功能。
线程:一个进程至少有一个线程,只有一个线程称为单线程,多个线程称为多线程。
2.并行和并发
并行和并发是发生在多线程情况下的一种现象。
并行是指两个或多个事件(线程),在同一时刻同时发生。
并发是指两个或多个事件,在同一时间段内发生。
3.线程的创建
在了解基础概念之后,接下来是线程的创建。线程的创建有三种方式:
(1)继承Thread类
第一步在需要使用线程的业务类中继承Thread类:
第二步重写Thread类中run方法:
第三步在run方法 中调用业务方法:(这步可省略。方法可以直接在run中写)
第四步;在测试类中创建业务对象并开启线程:
开启线程是使用Thread类中的start方法进行开启。
测试结果:
在不使用线程的情况下,threadService1执行完再执行threadService2,也就是说在控制台可以看到两次从0循环到99。
(2) 实现Runnable接口
第一步实现Runnable接口:
第二步重写run方法:
第三步在测试类中创建业务类对象:
第四步把业务类对象转换成Thread类对象(Runnable对象没有开启线程的方法,因此需要将其转换成Thread对象):
第五步开启线程:
测试结果:与继承Thread类的现象相同。
(3)实现callable接口 这种方式不常用(了解即可)
4.继承Thread类和实现Runnable接口这两种方式的区别
通过观察继承Thread类和实现Runnable接口这两种方式的实现过程,我们发现这两种方式非常相似,那么两者的不同之处在哪里呢。
在第一种(Thread)方式中,我创建了两个对象分别是threadService1和threadService2,也就是说两个线程分别对应了两个线程。这两个对象中的数据相互独立,互不影响(不能数据共享);
而第二种(Runnable)方式,我只创建了一个业务对象,然后用这一个对象创建了两个线程,也就是说,这两个线程中的对象是同一个,对象中的数据是共享的。
总结:Thread类线程,每开启一个线程都要创建一个新的该类对象,对象之间相互独立,数据和属性不能共享。
Runnable类线程,创建线程时,可以实现对同一对象开启多个线程,达到多线程之间数据共享。
5.线程中的常见操作
(1)SetName("线程名称")设置线程名称。
(2)getName()获取线程名称 。
对于继承Thread类的子类来说,获取线程名称可以直接调用getName()方法。
而实现Runnable接口的的类,则需要通过调用Thread类中的静态方法currentThread()来获取当前正在执行的线程对象,然后再调用getName()方法获取线程名称。
执行结果:
(3) setPriority(int a)设置优先等级(a的范围:1~10,值越大,优先级越高),线程默认的优先等级为5。
(3)getPriority() 获取优先等级。
6.线程状态
(1)就绪状态、运行状态、阻塞状态、销毁(死亡)状态。
就绪状态:当开启线程之后线程就进入了就绪状态。
运行状态:线程抢占到cpu之后,开始运行。
阻塞状态:正在执行的线程非正常结束运行,被阻塞的线程被唤醒之后重新进入就绪状态。
销毁状态:线程正常执行完毕。
(2)线程阻塞状态的原因
*线程睡眠sleep(millis);设置睡眠时间,单位毫秒。
*等待该线程执行完毕(死亡)join();该方法会使调用该方法的线程执行完毕之后其他线程才能执行。
*中断线程interrupt();
*线程等待wait();该方法通常与notify()或者notifyAll()搭配使用。该方法会导致当前线程等待,直到另一个线程调用notify()方法唤醒等待线程看,被唤醒的线程重新进入就绪状态。
7.守护线程
守护线程和普通线程的区别在于守护线程会随着主线程的结束而结束。