线程与进程
进程
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程
是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程。
线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程
线程调度
分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为 抢占式调度。
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使用率更高。
同步与异步&&并发与并行
同步: 排队执行 , 效率低但是安全.
异步: 同时执行 , 效率高但是数据不安全.
并发: 指两个或多个事件在同一个时间段内发生。
并行: 指两个或多个事件在同一时刻发生(同时发生)。
线程的三种实现方式
1.线程的实现方式一:
继承: Thread;类:
重写Run方法: 线程对象要想开启一条线程, start 方法。
start 方法会自动调用run方法。
为什么重写run方法?
因为Thread类当中提供的run方法,没有实现任何的操作。 返回了
start 方法和 run 方法之间的区别:
start方法: 会开启一条新的线程。
run方法: 直接调用run方法, 是属于对象的特有的某个方法。
2. 多线程的实现方式二:
实现Runnable接口, 重写run方法。
实现: Runable接口,此时该类不具备线程对象的特点,
但是run方法, run方法当中定义的内容,称之为线程任务。
要想使用对象具备线程特点,需要将线程任务对象作为参数传递给Thread 类。
步骤:
(1)定义类实现Runnable接口。 避免了单继承的局限。
(2)重写run方法。 run定义了线程任务。 同时被执行的代码。
(3)创建一个Thread类,然后将Runnable接口的实现类对象作为参数传递。
(4)调用start方法, 开启新的线程。
3.多线程的实现方式三:
实现Callable接口,实现call方法
线程安全问题
线程同步的一个前提:
多个线程在同步的时候,必须使用同一把锁。
当出现了数据安全问题后,加锁。
加锁完成后,数据安全的问题并没有得到解决。
原因: 多个线程之间并没有使用同一把锁。
解决: 多个线程之间要想解决数据安全问题,必须使用同一把锁。
解决方法有三个:1.使用同步代码块synchronized (o) {}
2.使用同步方法
3,使用显示锁Lock
l.lock();//上锁
l.unlock();//释放锁
线程间通信
Java实现多线程通信是通过系统方法实现的,只要是wait()、notify()和notifyAll()方法
wait()方法
wait()方法使得当前线程必须要等待,等到另外一个线程调用notify()或者notifyAll()方法。
线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。
注意:
要确保调用wait()方法的时候拥有锁,即wait()方法的调用必须放在synchronized方法或synchronized块中;
另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中不会释放掉对象的锁。
notify()方法
notify()方法通知等待监视器的线程,该对象的状态已经改变。会唤醒一个等待的线程。
注意:
如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关;
被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁;
被唤醒的线程将和其他线程以通常的方式进行竞争,来获得对象的锁。也就是说,被唤醒的线程并没有什么优先权,也没有什么劣势,对象的下一个线程还是需要通过一般性的竞争,也就是看优先级;
notify()方法应该是被拥有对象的锁的线程所调用。也就是和wait()方法一样,必须放在synchronized方法或synchronized块中。
notifyAll()方法
notifyAll()方法会唤醒从同一个监视器中用wait()方法退出的所有线程,使它们按照优先级顺序重新排队
线程池
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
线程池的好处 降低资源消耗。
提高响应速度。
提高线程的可管理性。
四种线程池:
1.缓存线程池
缓存线程池.
(长度无限制)
执行流程: 1. 判断线程池是否存在空闲线程
2. 存在则使用
3. 不存在,则创建线程 并放入线程池, 然后使用
2.定长线程池 长度是指定的数值
1. 判断线程池是否存在空闲线程
2. 存在则使用
3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
3.单线程线程池同上
4.周期性任务定长线程池
周期任务 定长线程池.
执行流程:
1. 判断线程池是否存在空闲线程
2. 存在则使用
3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 *
4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程