1.多线程的引入
如果程序只有一条执行路径,那么该程序就是单线程。 如果程序有多条执行路径,那么该程序就是多线程。
2.进程及线程的概述
要了解多线程,必须先了解线程。要想了解线程,必须先了解进程,因为线程是依赖进程而存在。 什么是进程?
进程:就是正在运行的程序。 进程是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。 多进程有什么意义呢?
单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。 举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。 现在的计算机都是支持多进程的,就可以在一个时间段内执行多个任务。可以提高CPU的使用率。 一边玩游戏,一边听音乐是同时进行的吗?不是,因为单CPU在某一个时间点上只能做一件事情。而我们在玩游戏,或者听音乐的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。 什么是线程?
在同一个进程内又可以执行多个任务,而这每一个任务我就可以看出是一个线程。 线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位。 单线程:如果程序只有一条执行路径。 多线程:如果程序有多条执行路径。 多线程有什么意义呢?
多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。 程序的执行其实都是在抢CPU的资源,CPU的执行权。 多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。 我们不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。
3.多线程举例及并发和并行的区别
多线程举例:扫雷程序,迅雷下载。 并行和并发的区别?
并行:逻辑上同时发生,指在某一个时间内同时运行多个程序。 并发:物理上同时发生,指在某一个时间点同时运行多个程序。
4.实现多线程Thread
如何实现?
由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。 而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。 Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。 但是,Java可以调用C/C++写好的程序来实现多线程程序。 由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,然后提供一些类供我们使用。这样就可以实现多线程程序了。
那么Java提供的类是什么呢?
方式1:继承Thread类
自定义类MyThread继承Thread类。 MyThread类里面重写run()?
不是类中的所有代码都需要被线程执行。 为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含哪些被线程执行的代码。 创建对象。 启动线程。 面试题:run()和start()的区别?
run():仅仅是封装被线程执行的代码,直接调用时普通方法。 start():首先启动了线程,然后再由JVM去调用该线程的run()方法。
IllegalThreadStateException:非法线程状态异常。相当于某线程被调用了两次,而不是两个线程启动。
5.Java程序运行原理和JVM的启动是多线程的
java程序的运行原理
有java命令启动JVM,JVM启动就相当于启动了一个进程。接着由该进程创建了一个主线程去调用main方法。
JVM虚拟机的启动是多线程的。
原因:垃圾回收线程也要先启动,否则很容易出现内存溢出。 现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,所以JVM的启动是多线程的。
6.获取和设置线程对象名称
如何获取线程对象的名称?
public final String getName();
名称为什么是:Thread-? 编号
class Thread{
private char name[];
public Thread(){
init(null,null,"Thread-"+nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name, long stackSize){
init(g, target, name, stackSize, null);
}
private void init(ThreadGroup g, Runnable target, String name, long stackSize,AccessControlContext acc){
this.name = name.toCharArray();
}
public final void setName(String name){
this.name = name.toCharArray();
}
private static int threadInitNumber;//return 0,1,2
private static synchronized int nextThreadNum(){
return threadInitNumber++;//return 0,1
}
public final String getName(){
return String.valueOf(name);
}
}
class MyThread extends Thread{
public MyThread(){
super();
}
}
如何设置线程对象的名称?
public final void setName(String name):设置线程对象的名称
获取main方法所在的线程对象的名称,该怎么办?(针对不是Thread类的子类中如何获取线程对象名称呢?)
Thread类提供了一个方法 public static Thread currentThread();返回当前正在执行的线程对象 Thread.currentThread().getName();
7.线程调度及获取和设置线程的优先级
获取线程对象的优先级?
public final int getPriority();返回线程对象的优先级
设置线程对象的优先级?
public final void setPriority(int newPriority); 更改线程的优先级 注意: 1. 线程默认优先级是5。 2. 线程优先级的范围是:1-10. 3. 线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。
IllegalArgumentException:非法参数异常:抛出的异常表明向方法传递了一个不合法或不正确的参数。
8.线程控制之…
线程休眠:public static void sleep(long millis); 线程加入:public final void join(); 等待该线程终止 线程礼让:public static void yield(); 暂停当前正在执行的线程对象,并执行其他线程。 守护线程(后台线程):
public final void setDaemon(boolean on); 将该线程标记为守护线程或用户线程。 当正在运行的线程都是守护线程是,Java虚拟机退出,该方法必须在启动线程前调用。 举例:坦克大战。
中断线程:
public final void stop(); 让线程停止,过时了,但是还可以使用。 public void interrupt(); 中断线程,把线程的状态终止,并抛出一个InterruptedException
9.线程生命周期图解(面试题)
面试题:线程的生命周期?
新建:创建线程对象。 就绪:有执行资格,没有执行权。 运行:有执行资格,有执行权。
阻塞:由于一些操作让线程处于了该状态。没有执行资格,没有执行权,而另一些操作却可以把它激活,激活后处于就绪状态。 死亡:线程对象变成垃圾,等待被回收。
图解
10.多线程两种方式的图解比较及区别
方式一:继承Thread类
自定义类MyThread继承Thread类 在MyThread类中重写run() 创建MyThread类的对象 启动线程对象
方式二:实现Runnable接口
自定义类MyRunnable实现Runnable接口 在MyThread里面重写run() 创建MyRunnable类的对象 创建Thread类的对象,并把上一步骤的对象作为构造参数传递
为什么要Runnable接口的实现方式
可以避免由于Java单继承带来的局限性。 适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效的分离,较好的体现了面向对象的设计思想。
11.线程安全问题的产生原因分析
判断一个程序是否会有线程安全问题的标准
是否是多线程环境 是否有共享数据 是否有多条语句操作共享数据
怎么解决?
思想:把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的的时候别人不能来执行。Java给我们提供了:同步机制。 同步代码块:
synchronized(对象){
需要同步的代码;
}
A:对象是什么呢?
- 随便创建一个试一试
B:需要同步的代码是哪些呢?
- 把多条语句操作共享数据的代码的部分给包起来。
注意:同步解决安全问题的根本原因在那个对象上,该对象如同锁的功能。(多线程必须是同一把锁)
同步的特点:
前提:多个线程 解决问题要注意:多个线程使用的是同一个锁对象。 同步的好处:同步出现解决了多线程的安全问题。 同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
A:同步代码块的锁对象是谁呢?
B:同步方法的格式及锁对象问题?
C:静态方法及锁对象问题?
静态方法的锁对象是谁呢? 类的字节码文件对象(反射)
12.卖票的一些问题
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
加入延迟之后,就产生了两个问题:
相同的票卖了多次
出现了负票数
package com.wal1;
public class SellTicket implements Runnable {
/*private int tickets = 100;
private Object obj = new Object();
private int x = 0;
@Override
public void run() {
while(true){
if(x % 2 == 0){
synchronized(this){
if(tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在抢第"+tickets--+"张票");
}
}
}else{
sellTicket();
}
x++;
}
}
public synchronized void sellTicket(){
if(tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在抢第"+tickets--+"张票");
}
}*/
private static int tickets = 100;
//private Object obj = new Object();
private int x = 0;
@Override
public void run() {
while(true){
if(x % 2 == 0){
synchronized(SellTicket.class){
if(tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在抢第"+tickets--+"张票");
}
}
}else{
sellTicket();
}
x++;
}
}
public static synchronized void sellTicket(){
if(tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在抢第"+tickets--+"张票");
}
}
}
package com.wal1;
public class SellTicketDemo {
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread st1 = new Thread(st, "窗口一");
Thread st2 = new Thread(st, "窗口二");
Thread st3 = new Thread(st, "窗口三");
st1.start();
st2.start();
st3.start();
}
}
13.线程安全类回顾
线程安全的类
StringBuffer sb = new StringBuffer();
Vector<String> v = new Vector<String>();
Hashtable<String,String> h = new Hashtable<String,String>();
//Vector即使线程安全也不用你。
//那用谁呢?
//public static <T> list <T> synchronizedList(List<T> list)
List<String> list1 = new ArrayList<String>();//线程不安全
List<String> list2 = new Collections.synchronizedList(new ArrayList<String>());//线程安全