线程
什么是线程
如果程序只有一条执行路径,那么该程序就是单线程程序
如果程序有多条执行路径,那么该程序就是多线程程序。
进程与线程的描述
线程是依赖于进程存在的
什么是进程
通过任务管理器看到了进程的存在
但是,我们只发现了正在运行的进程,没有运行的程序,进程中就没有
进程:就是正在运行的程序
正在运行的程序,是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源
多进程
单进程,是计算机只能做一件事情,但是呢,现在,我们的计算机都可以做多件事情,
比如:一边玩游戏(游戏进程),一边听音乐(音乐进程)
也就是说,现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务
那么:单CPU的时候,一边玩游戏一边听音乐是同时在进行的吗?
不是,因为单CPU在某个时间点上只能做一件事情
但是为什么我们感觉是同时进行的呢?
是因为CPU在做着程序之间高效切换让我们觉得是在同时进行
什么是线程
在同一个进程中又可以执行多个任务,而这每一个任务,我们看作线程
线程:是进程中的单个顺序控制流,是一条执行路径,是程序的执行单元,执行路径是程序使用CPU的最基本单位
单线程:一个进程只有一条执行路径
多线程:一个进程有多条执行路径
多线程有什么意义呢?
多线程的存在,不是提高程序的执行速度,是为了提高应用程序的使用率存在的
在学习过程中,我们常见两种使用CPU的方式,一种是分片调度,一种是抢占式调度,而我们的Java是抢占式调度
多线程就是某个进程在抢占资源的时候,执行路径比较多,有着更高的几率抢到,但是我们不能保证哪一个线程能够在哪一个时刻可以抢到CPU资源
所以线程的执行具有随机性
并发与并行的描述
并行:指的是逻辑上同时发生,指在某一个时间段内同时运行多个程序
并发:指的是物理上同时发生,指在某一个时间点内同时运行多个程序
Java程序的运行原理
由Java命令去启动一个JVM,JVM启动就相当于启动了一个进程
接着由该进程去创建一个主线程去调用main方法
思考:JVM虚拟机启动的时候是单线程还是多线程呢
多线程
因为:垃圾回收线程也是需要启动的,否则就会出现内存溢出
所以JVM虚拟机启动的时候,最低启动两个线程,所以JVM虚拟机启动的时候是多线程
实现多线程
如何实现多线程呢
由于线程是依赖于进程存在的,所以我们应该先创建一个进程出来,而进程最终是由系统创建的,所以我们就要想应该去调用系统的功能去创建进程
但是,Java并不能直接调用系统功能,所以我们就没办法实现多线程程序
但是,Java可以去调用C/C++写好的程序来实现多线程程序
由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西就可以了
然后提供一些类给我们使用,我们就可以创建多线程程序了
Java提供的类是什么呢
Thread类
通过查看API文档,我们知道了有两种实现多线程程序的方式
方式一:继承Thread类实现多线程
步骤:
自定义一个MyThread类继承Thread类
自定义重写Thread类中的run方法(类中的代码不是所有代码都需要被线程执行,只有当需要被线程执行的时候,再把代码加入到run方法中)
创建对象
启动线程
在多线程中获取和设置线程名称
- 获取线程对象的名称:
public final String getName() 返回此线程的名称 - 设置线程对象的名称:
public final void setName(String name)将此线程的名称改为name
public static void main(String[] args) {
//创建线程对象
//无参构造创建对象,开发中推荐这一招
// MyThread2 m1 = new MyThread2();
// MyThread2 m2 = new MyThread2();
// m1.setName("zhangsan");
// m2.setName("lisi");
//有参构造方法给线程对象起名字
// MyThread2 m3 = new MyThread2("wangwu");
// MyThread2 m4 = new MyThread2("zhaoliu");
// m3.start();
// m4.start();
// System.out.println(m1.getName());
// m1.start();
// m2.start();
//获取main方法所在的线程的名称
//Thread提供了一个方法给我们,可以获取main方法的线程,也就是主线程
System.out.println(Thread.currentThread().getName());
}
线程的优先级
如何获取线程对象的优先级
public final int getPriority()返回此线程的优先级
如何设置线程对象的优先级
public final void setPriority(int newPriority)更改此线程的优先级
MIN_PRIORITY到MAX_PRIORITY之间(1-10)
IllegalArgumentException:抛出表示一种方法已经通过了非法或不正确的参数。非法参数异常
注意:
线程的默认优先级是5
线程的优先级范围是1-10
线程的优先级仅仅代表获取CPU时间片的几率,但是不是绝对会先获取
public final void join():其他线程等待这个线程死亡
public final void setDaemon() 守护线程
线程的中断
public final void stop():让线程停止(该方法已经过时了,但是还能用)
public void interrupt():中断这个线程,并且抛出异常
InterruptedException: sleep interrupted
注意:stop是直接让方法结束了,interrupt是结束之后抛出异常
线程的生命周期
方式二:实现Runnable接口
步骤:
- 自定义MyRunnable类实现Runnable接口
- 重写run方法
- 创建MyRunnable对象
- 创建Thread对象,并把第三步的对象当作参数传入构造方法中
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<4;i++){
//由于实现的Runnable接口没有getName()方法,就不能直接使用Thread类的方法了
//但是可以间接使用
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class MyRunnableDemo {
public static void main(String[] args) {
MyRunnable my = new MyRunnable();
Thread t1 = new Thread(my);
Thread t2 = new Thread(my);
t1.start();
t2.start();
}
}
//Thread-0:0
//Thread-1:0
//Thread-1:1
//Thread-0:1
//Thread-1:2
//Thread-1:3
//Thread-0:2
//Thread-0:3
线程安全问题
- 是否有多线程环境
- 是否有共享数据
- 是否有多条语句操作共享数据
同步机制
同步代码块:
格式:
synchronized(对象){ //把需要同步的数据都放在这里面
需要同步的代码块;
}
1、对象是什么呢
我们可以随便创建一个对象试试
2、需要同步的代码块是什么呢
多条语句操作共享数据的代码部分包起来
同步的好处:
同步的出现解决了多线程的安全问题
同步的弊端:
当线程相当多的时候,因为每个线程都会取判断同步上的锁,这样会非常耗费资源,无形中降低了程序运行的效率
同步代码块的锁对象是谁呢
任意对象
同步方法:
就是把同步关键字加到方法上
private synchronized 加到权限修饰符后面
同步方法的锁是什么呢
this
线程安全的类
- Vector 不过,虽然它是线程安全的,但是我们也不用
- StringBuffer
- Hashtable