1、进程的概述和多进程的意义
2、线程的概述和多线程的意义
3、JVM运行原理以及JVM启动的线程探讨
4、实现多线程
5、线程调度
6、线程控制
1、进程的概述和多进程的意义
A:线程和进程
要想说线程,首先必须得聊聊进程,因为线程是依赖于进程存在的。
B:进程概述
什么是进程呢?通过任务管理器我们就可以看到进程的存在。
概念:进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。
C:多进程的意义
单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音乐进程),
所以我们常见的操作系统都是多进程操作系统。比如:Windows,Mac和Linux等,能在同一个时间段内执行多个任务。
对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。
因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,
所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。多进程的作用不是提高执行速度,而是提高CPU的使用率。
2、线程的概述和多线程的意义
A:什么是线程
在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。是程序使用CPU的基本单位。
B:多线程有什么意义呢?
多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。
那么怎么理解这个问题呢?
我们程序在运行的使用,都是在抢CPU的时间片(执行权),如果是多线程的程序,那么在抢到
CPU的执行权的概率应该比较单线程程序抢到的概率要大.那么也就是说,CPU在多线程程序
中执行的时间要比单线程多,所以就提高了程序的使用率.但是即使是多线程程序,那么他们
中的哪个线程能抢占到CPU的资源呢,这个是不确定的,所以多线程具有随机性.
C:大家注意两个词汇的区别:并行和并发。
前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
后者是物理上同时发生,指在某一个时间点同时运行多个程序。
3、 JVM运行原理以及JVM启动的线程探讨
A:Java程序运行原理
Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。
该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
所以 main方法运行在主线程中。
B:JVM的启动是多线程的吗: JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
4、实现多线程
A:如何实现多线程:
如何实现呢?
由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
但是Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。
由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,
然后提供一些类供我们使用。我们就可以实现多线程程序了。
实现多线程有俩种方式:
1、继承Tread类,并重写run方法
2、实现runnable方法,并重写run方法
Thread类的基本获取和设置方法
public final String getName()//获取线程名称
public final void setName(String name)//设置线程名称
其实通过构造方法也可以给线程起名字
public static Thread currentThread()//获取当前执行的线程
public final int getPriority() //获取线程的优先级
public final void setPriority(int newPriority)//设置线程的优先级
public static void sleep(long millis) 线程休眠
加入线程: public final void join()
意思就是: 等待该线程执行完毕了以后,其他线程才能再次执行
注意事项: 在线程启动之后,在调用方法
// 创建三个线程对象
MyThread mt1 = new MyThread(“李渊”) ;
MyThread mt2 = new MyThread(“李元霸”) ;
MyThread mt3 = new MyThread(“李世民”) ;
// 启动线程
mt1.start() ;
/**
* 我们都只要"李渊"应该是"李元霸"和"李世民"他爹,那么在执行的时候,我们需要先将
* 李渊线程执行完毕以后再执行其他的线程,那么怎么完成这个事情呢?
* 我们需要使用一个线程的方法:
* public final void join() 等待该线程执行完毕以后再执行其他线程。
*
*/
mt1.join() ;//线程加入 让李渊线程先执行完 其他两个线程再去执行
mt2.start() ;
mt3.start() ;
礼让线程: public static void yield(): 暂停当前正在执行的线程对象,并执行其他线程。
B:案例演示: 礼让线程
按照我们的想法,这个礼让应该是一个线程执行一次,但是通过我们的测试,效果好像不太明显.
那是为什么呢?
这个礼让是要暂停当前正在执行的线程,这个暂停的时间是相当短的,如果在这个线程暂停完毕以后,其他的线程还没有
抢占到CPU的执行权,那么这个时候这个线程应该再次和其他线程抢占CPU的执行权.
守护线程: public final void setDaemon(boolean on):
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
public final void stop(): 停止线程的运行
public void interrupt(): 中断线程(这个翻译不太好),查看API可得当线程
A:案例演示
需求:某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
A:加入延迟
我们前面讲解过电影院售票程序,从表面上看不出什么问题,但是在真实生活中,
售票时网络是不能实时传输的,总是存在延迟的情况,所以,在出售一张票以后,需要一点时间的延迟
改实现接口方式的卖票程序,每次卖票延迟100毫秒
B:出现问题了问题
public class SellTickets extends Thread {
private static int tickets = 100 ;
private static final Object obj = new Object() ;//这个对象要定义在成员位置设置成静态的 让所有线程对象共享
@Override
public void run() {
// 卖票
while(true){
// 同步代码块
synchronized(obj){//这个对象相当于一把锁 哪个线程先进来先锁住 其他线程进不来 等我进来的这个线程执行完了 释放了这个锁 其他线程才能进来
if( tickets > 0 ){
// 模拟网络延迟
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + “正在出售” + (tickets–) + “张票”);
}
}
}
}
}
public class SellTicketsTest {
public static void main(String[] args) {
// 创建对象
SellTickets t1 = new SellTickets() ;
SellTickets t2 = new SellTickets() ;
SellTickets t3 = new SellTickets() ;
// 给线程设置名称
t1.setName("窗口1") ;
t2.setName("窗口2") ;
t3.setName("窗口3") ;
// 启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
}
线程安全问题的产生原因分析:
判断一个多线程应用程序是否有问题的标准:
a: 是否是多线程环境
b: 是否存在共享数据
c: 是否存在多条语句同时操作共享数据
我们现在这个程序是存在问题的,因为它满足上面的标准,那么我们只要将这个标准打乱,那么我们就可以解决这个问题.
而上面的标准中a , b是不能打乱的,因此我们只能对c做处理,关键是怎么处理? 如果我们把操作共享数据的多条语句看做
成一个整体,当一个线程执行这个整体的时候,其他的线程处于等待状态,也就说当一个线程执行这个整体的时候,其他线程
不能进行执行,那么怎么做到这个一点呢?
需要使用同步代码块:
格式:
synchronized(对象){//不能在括号了直接new 对象 new 了 就没效果
要被同步的代码 ;
}
*
这个同步代码块保证数据的安全性的一个主要因素就是这个对象 注意这个对象 要定义为静态成员变量 才能被所有线程共享
需要这个对象被所有的线程对象所共享
这个对象其实就是一把锁.
这个对象习惯叫做监视器
锁定义在方法上的俩种情况:
1、 同步方法 同步方法的锁对象: 是this
// public synchronized void sellTickets(){
2、静态同步方法的锁对象:就是当前类对应的字节码文件对象