多线程概述
多线程引入
把备注部分的代码通过画图解释一下调用流程。这个程序只有一个执行流程,所以这样的程序就是单线程程序。
假如一个程序有多条执行流程,那么,该程序就是多线程程序。
接下来我们来看看到底什么是多线程多线程概述
进程:
正在运行的程序,是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。
线程:
是进程中的单个顺序控制流,是一条执行路径
一个进程如果只有一条执行路径,则称为单线程程序。
一个进程如果有多条执行路径,则称为多线程程序。举例
扫雷游戏,迅雷下载等
1:要想说线程,首先必须得聊聊进程,因为线程是依赖于进程存在的。
2:那么,什么是进程呢?通过任务管理器我们就可以看到进程的存在。
给出一个概念:进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
3:多进程有什么意义呢?
单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音乐进程),所以我们常见的操作系统都是多进程操作系统。比如:Windows,Mac和Linux等,能在同一个时间段内执行多个任务。
对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。
因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。
多进程的作用不是提高执行速度,而是提高CPU的使用率。
4:那么什么又是线程呢?
在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。线程是程序中单个顺序的控制流,是程序使用CPU的基本单位。
5:多线程有什么意义呢?
多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。
而多线程却给了我们一个错觉:让我们认为多个线程是并发执行的。其实不是。
因为多个线程共享同一个进程的资源(堆内存和方法区),但是栈内存是独立的,一个线程一个栈。所以他们仍然是在抢CPU的资源执行。一个时间点上只有能有一个线程执行。而且谁抢到,这个不一定,所以,造成了线程运行的随机性。
6:那么什么又是并发呢?
大家注意两个词汇的区别:并行和并发。
前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
后者是物理上同时发生,指在某一个时间点同时运行多个程序。
那么,我们能不能实现真正意义上的并发呢,是可以的,多个CPU就可以实现,不过你得知道如何调度和控制它们。
请
7:那么,我们来举例说说什么是进程,什么是线程。
扫雷游戏,迅雷下载等。
Java程序运行原理
Java程序运行原理
java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
思考:
jvm虚拟机的启动是单线程的还是多线程的?
JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
多线程的实现方案1
继承Thread类
如何获取和设置线程名称
Thread类的基本获取和设置方法
public final String getName() public final void setName(String name)
其实通过构造方法也可以给线程起名字
思考:
如何获取main方法所在的线程名称呢?
public static Thread currentThread()
这样就可以获取任意方法所在的线程名称
线程调度
- 假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到
CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的呢? 线程有两种调度模型:
分时调度模型 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
抢占式调度模型 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。Java使用的是抢占式调度模型。
演示如何设置和获取线程优先级
public final int getPriority()
public final void setPriority(int newPriority)
线程控制
我们已经知道了线程的调度,接下来我们就可以使用如下方法对象线程进行控制
线程休眠
public static void sleep(long millis)
线程加入
public final void join()
线程礼让
public static void yield()
后台线程
public final void setDaemon(boolean on)
中断线程
public final void stop()
public void interrupt()
线程的生命周期图
多线程的实现方案2
实现Runnable接口
实现接口方式的好处
可以避免由于Java单继承带来的局限性。
适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
解决线程安全问题实现1
同步代码块
格式:
synchronized(对象){需要同步的代码;}
同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
同步的特点
同步的前提
多个线程
多个线程使用的是同一个锁对象同步的好处
同步的出现解决了多线程的安全问题。
同步的弊端
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
解决线程安全问题实现2
同步方法
就是把同步关键字加到方法上
同步方法的锁对象是什么呢?
如果是静态方法,同步方法的锁对象又是什么呢?
那么,我们到底使用谁?
如果锁对象是this,就可以考虑使用同步方法。
否则能使用同步代码块的尽量使用同步代码块。
JDK5中Lock锁的使用
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock
void lock()
void unlock()ReentrantLock
死锁问题
同步弊端
效率低
如果出现了同步嵌套,就容易产生死锁问题死锁问题及其代码
是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
线程的状态转换图
线程组
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
默认情况下,所有的线程都属于主线程组。
public final ThreadGroup getThreadGroup()
我们也可以给线程设置分组
Thread(ThreadGroup group, Runnable target, String name)
线程池
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newCachedThreadPool()
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)
多线程程序实现方案3
- 实现Callable接口
好处:
可以有返回值
可以抛出异常弊端:
代码比较复杂,所以一般不用
匿名内部类方式使用多线程
- 匿名内部类方式使用多线程
- new Thread(){代码…}.start();
- New Thread(new Runnable(){代码…}).start();
new Thread() {
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + "---" + x);
}
};
}.start();
new Thread(new Runnable() {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + "---"
+ x);
}
}
}).start();
定时器
- 定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能
Timer
public Timer()
public void schedule(TimerTask task, long delay)
public void schedule(TimerTask task,long delay,long period)TimerTask
public abstract void run()
public boolean cancel()开发中
Quartz是一个完全由java编写的开源调度框架。
多线程面试题
多线程有几种实现方案,分别是哪几种?
同步有几种方式,分别是什么?
启动一个线程是run()还是start()?它们的区别?
sleep()和wait()方法的区别
为什么wait(),notify(),notifyAll()等方法都定义在Object类中
wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
1,这些方法存在与同步中。
2,使用这些方法时必须要标识所属的同步的锁。
3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。线程的生命周期图
Runtime类的概述和使用
Runtime类概述
每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。
应用程序不能创建自己的 Runtime 类实例。Runtime类使用
public Process exec(String command)