Java中的多线程
什么是进程?
进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
什么是线程
每个运行的程序都是一个进程,在一个进程中还可以有多个执行单元同时运行,这些执行单元可以看作程序执行的一条条线索,被称为线程。
线程与进程的关系
线程是CPU调度的最小单元,同时线程是一种有限的系统资源,即线程不可能无限制地产生,并且线程的创建和销毁都有相应的开销。而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
- 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
- 处理机分给线程,即真正在处理机上运行的是线程。
- 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
- 线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
多线程的实现方案
一丶继承Thread类,重写run()方法
- 定义一个类继承Thread类
- 覆盖Thread类中的run方法
- 直接创建Thread的子类对象创建线程
- 调用start方法开启线程并调用线程的任务run方法执行
二丶实现Runnable接口
- 定义类实现Runnable接口
- 覆盖接口中的run方法,将线程的任务代码封装到run方法中
- 通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务
- 调用线程对象的start方法开启线程
run()和start()方法的区别
- run():仅仅是封装了被线程执行的代码,直接调用就是普通方法
- start():首先是启动了线程,然后再由jvm去调用了该线程的run()方法,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。
Thread和Runnable的区别
- Thread单继承,Runnable可以多实现
- Runnable可以实现资源共享
- Thread定义线程和使用线程在一起
- Runnable定义线程和使用线程分开,解耦
同步和死锁
为什么要同步?
几个对象都要用同一个资源, 需要共享数据,Java线程共享数据需要同步的根本原因在于Java内存的设计。
如何同步?实现同步的方法?
同步的内部机制:互斥锁
关键字:synchronized
同步代码块:
在代码块声明上加上synchronized
synchronized (锁对象) {
可能会产生线程安全问题的代码
}同步方法:在方法声明上加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
死锁
死锁:
同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。
synchronzied(A锁){
synchronized(B锁){}
}过多的同步就有可能导致死锁