1、多线程:一个应用程序有多条执行路径
进程:正在运行的应用程序(是系统进行资源分配和调用的独立单元,每一个进程有它自己的内存空间和系统资源)
线程:进程的执行单元(程序执行的任务),执行路径
单线程:一个应用程序只有一条执行路径
多线程:一个应用程序有多条执行路径(一个程序执行多个任务)
并行和并发:
前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
后者是物理上同时发生,指在某一个时间点同时运行多个程序。
多进程的意义?
提高CPU的使用率
多线程的意义?
提高应用程序的使用率
2、Java程序的运行原理及JVM的启动是多线程的吗?
A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程,主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。
B:JVM的启动是多线程的,它最少有两个线程启动,主线程和垃圾回收线程。
3、多线程的实现方案(掌握)
A:继承Thread类
1)自定义类MyThread继承Thread类
2)MyThread类里面重写run()
run+alt+/
为什么?
不是类中所有代码都需要被线程执行(方法包含 需要线程执行的代码(比较耗时的代码)),为了区分哪些代码能够被线程执行。
getName(); //获取线程名称(在run()方法中)
Thread.currentThread().getName(); //通用获取线程名称
3)创建对象
MyThread mt = new MyThread();
4)启动线程
mt.start();
注意:重复调用run方法是单线程
run()和start()的区别?
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,然后再由jvm去调用该线程的方法
IllegalThreadStasteException:指非法线程状态异常
why?
两个start()相当于是mt线程被调用了两次。而不是两个线程启动。
解决:创建两个线程对象
MyThread mt2 = new MyThread();
mt.setName("baron"); //设置线程名称
mt.start();
mt2.start();
B:实现Runnable接口(常用)
1)自定义类MyRunnable类实现Runnable接口
2)重写run()方法
3)创建MyRunnable类对象
4)创建Thread类对象,并把3)步骤的对象作为构造参数传递
C:有了方式一为什么还有方式二?
解决了Java单继承带来的局限性
适合多个相同程序的代码去处理一个资源的情况,把线程同程序的代码,数据有分离。较好的体现了面向对象的设计思想。
4、线程的调度和优先级问题
假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只 有得到 CPU时间片,也就是使用权,才可以执行指令。
A:线程的两种调度模型
a:分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
b:抢占式调度 (Java采用的是该调度方式):优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
B:获取和设置线程优先级
a:默认是5
b:范围是1-10
c:获取线程对象优先级
public final int getPriority()
mt.getPriority();
d:设置对象优先级
mt.setPriority();
IllegalArgumentException:非法参数异常
线程优先级高仅仅表示线程获取的CPU时间片的几率高,但要在次数比较多,或者多次运行的时候才能看到比较好的效果
5、线程的控制(常见方法)
A:休眠线程
public static void sleep(long millis)
Thread.sleep(1000); //在run()或main()方法中休息一秒
B:加入线程
public final void join() //等待线程终止(意思是其它线程等待我执行完毕后才可执行)
mt.join();
C:礼让线程
public static void yield():暂停当前正在执行的线程对象,并执行其它线程。
Thread.yield(); //在run()方法中让多个线程的执行更加和谐,但不能靠它保证一人一次。
D:后台线程
public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程,当正在运行的线程是守护线程时,Java虚拟机退出,该方法必须在启动线程前调用。
mt.setDaemon(true);
mt2.setDaemon(true);
E:终止线程(掌握)
public final void stop():过时,但是还可以用
mt.stop();
public void interrupt():终止线程,把线程状态终止,并抛出InterruptedException
mt.interrupt();
6、线程的生命周期(参照 线程生命周期图解.bmp)
A:新建:创建线程对象
B:就绪:有执行资格,无执行权限
C:运行:有执行资格,有执行权限
D:阻塞:由于一些操作让线程处于该状态,没有执行资格,没有执行权限。一些操作可将其激活,激活后处于就绪状态。
E:死亡:线程对象变成垃圾,等待被回收
7、电影院卖票程序的实现
某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
A:继承Thread类
B:实现Runnable接口
8、电影院卖票程序出问题
A:为了更符合真实的场景,加入了休眠100毫秒。
B:卖票问题
a:同票多次
b:负数票
9、多线程安全问题的原因(也是我们以后判断一个程序是否有线程安全问题的依据)
A:是否有多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据
10、同步解决线程安全问题
A:同步代码块
synchronized(对象) {
需要被同步的代码;
}
这里的锁对象可以是任意对象。
B:同步方法
把同步加在方法上。
这里的锁对象是this
C:静态同步方法
把同步加在方法上。
这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)
例子:
@Override
public void run() {
while (true) {
if(x%2==0){
synchronized (SellTicket.class) { //例子B换成this
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票 ");
}
}
}else {
sellTicket();
}
x++;
}
}
private static synchronized void sellTicket() {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票 ");
}
}
}
D:同步的前提
多个线程
多个线程使用的是同一个锁对象
E:同步的弊端:
当线程较多时,因为每个线程都会去判断同步上的锁,很耗资源,无形中降低程序运行效率
11、回顾以前的线程安全的类
A:StringBuffer
B:Vector
C:Hashtable
D:如何把一个线程不安全的集合类变成一个线程安全的集合类
用Collections工具类的方法即可。
// Vector是线程安全的时候才去考虑使用的,但是我还说过即使要安全,我也不用你
// 那么到底用谁呢?
// public static XXXX synchronizedXXXX(XXXX XXXX)
List list1 = new ArrayList();// 线程不安全
List list2 = Collections.synchronizedList(new ArrayList()); // 线程安全