一、进程和线程概念
1.1 进程和线程
线程:线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
进程:进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。
进程和线程的区别:
线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。从另一角度来说,进程属于操作系统的范畴,主要是同一段时间内,可以同时执行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段。
1.2 多线程是什么,为什么必要?
多线程:多线程就是几乎同时执行多个线程(一个处理器在某一个时间点上永远都只能是一个线程!即使这个处理器是多核的,除非有多个处理器才能实现多个线程同时运行。)。几乎同时是因为实际上多线程程序中的多个线程实际上是一个线程执行一会然后其他的线程再执行,
为什么必要:
- 使用线程可以把占据长时间的程序中的任务放到后台去处理
- 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
- 程序的运行速度可能加快
二、使用多线程方式
2.1 继承Thread类
1 | public class MyThread extends Thread { |
2 | |
3 | public void run() { |
4 | super.run(); |
5 | System.out.println("MyThread"); |
6 | } |
7 | } |
线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run方法。(mythread.start();)
2.2 实现Runnable接口
推荐实现Runnable接口方式开发多线程,因为Java单继承但是可以实现多个接口。
1 | public class MyRunnable implements Runnable { |
2 | |
3 | public void run() { |
4 | System.out.println("MyRunnable"); |
5 | } |
6 | } |
1 | public class Run { |
2 | |
3 | public static void main(String[] args) { |
4 | Runnable runnable=new MyRunnable(); |
5 | Thread thread=new Thread(runnable); |
6 | thread.start(); |
7 | System.out.println("运行结束!"); |
8 | } |
9 | } |
运行结果:
1 | 运行结束 |
2 | MyRunnable |
三、实例变量和线程安全
3.1 不共享数据的情况
每个线程都有一个属于自己的实例变量count,它们之间互不影响。
3.2 共享数据的情况
在大多数jvm中,count–的操作分为如下下三步:
- 取得原有count值
- 计算i -1
- 对i进行赋值
所以多个线程同时访问时出现问题就是难以避免的了。
解决办法:在run方法前加上synchronized关键字即可得到正确答案。
四、常用方法
currentThread():返回对当前正在执行的线程对象的引用。
getId():返回此线程的标识符
getName():返回此线程的名称
getPriority():返回此线程的优先级
isAlive():测试这个线程是否还处于活动状态。
(活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备运行的状态。)
sleep(long millis):使当前正在执行的线程以指定的毫秒数“休眠”(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
interrupt():中断这个线程。
interrupted() 和isInterrupted():
interrupted():测试当前线程是否已经是中断状态,执行后具有将状态标志清除为false的功能
isInterrupted(): 测试线程Thread对相关是否已经是中断状态,但部清楚状态标志
setName(String name):将此线程的名称更改为等于参数 name 。
isDaemon():测试这个线程是否是守护线程。
setDaemon(boolean on):将此线程标记为 daemon线程或用户线程。
join():
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行
yield():yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU时间。注意:放弃的时间不确定,可能一会就会重新获得CPU时间片。
setPriority(int newPriority):更改此线程的优先级
五、如何停止一个线程?
使用interrupt()方法
for循环虽然停止执行了,但是for循环下面的语句还是会执行,说明线程并未被停止。
使用return停止线程
六、线程的优先级
每个线程都具有各自的优先级,线程的优先级可以在程序中表明该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定首先使哪个线程进入运行状态。但这个并不意味着低优先级的线程得不到运行,而只是它运行的几率比较小,如垃圾回收机制线程的优先级就比较低。所以很多垃圾得不到及时的回收处理。
线程优先级具有继承特性比如A线程启动B线程,则B线程的优先级和A是一样的。
线程优先级具有随机性也就是说线程优先级高的不一定每一次都先执行完。
七、多线程分类
7.1 分类
用户线程:运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程
守护线程:运行在后台,为其他前台线程服务.也可以说守护线程是JVM中非守护线程的 “佣人”。
特点:一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作
应用:数据库连接池中的检测线程,JVM虚拟机启动后的检测线程
最常见的守护线程:垃圾回收线程
7.2 设置守护线程
可以通过调用Thead类的setDaemon(true)方法设置当前的线程为守护线程
注意事项:
- setDaemon(true)必须在start()方法前执行,否则会抛出IllegalThreadStateException异常
- 在守护线程中产生的新线程也是守护线程
- 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑
参考: