多线程
1.并行和并发
并行:在同一时刻,有多个任务同时执行
并发:在同一个时间段内,有多个任务"同时"执行。在同一时刻只能操作同一个任务
执行单元 --- 执行路径 -- 执行任务 -- 执行事件 说的是同一个东西
单核 CPU 在某一时刻上 只执行 一个路径(任务) 一个线程
我们看到的同时执行 其实就是并发
JAVA中 线程的调度模式 抢占式
2.进程和线程:
进程
正在运行的程序
当一个程序开始运行,进入内存之后 就产生了进程
创建---运行---消亡
线程
进程内部的执行单元,执行路径
一个进程中至少有一个执行路径 也就是说 一个进程中至少有一个线程
也可以有多条执行路径,也就是说可以有多个线程并发的执行,那么这样的程序就成为多线程程序
迅雷 软件 打开 进入内存----------形成了一个进程
我们可以多任务下载
多个任务下载 其实就是开启了多个 执行路径
那么 就是说 迅雷程序执行的时候 并发的执行了 多个线程
迅雷程序 就是 多线程程序
进程和线程的区别:
进程:有独立的内存空间(栈和堆),就是每一个应用程序都有独立的内存空间(栈和堆)
线程:每一个线程都是一个独立的栈空间,如果有多个线程那么这些线程共享同一个堆(必须是同一个进程中)
在同一个执行路径中(栈),执行代码都是从上至下执行。
3.如何开启新的线程
第一种方式:
1:创建自定义类 继承Thread
2:重写run方法 写新的线程执行的代码
3:创建自定义类对象 调用strat方法
第二种方式:
1: 创建自定义类 实现Runnable接口 (线程目标接口 线程任务接口)
2: 实现run方法 (定义线程要执行的任务)
3: 创建自定义类对象,并将此对象作为 Thread线程对象的构造参数 传递
4: 使用线程对象调用start方法 开启新的线程
Thread(Runnable target) 创建一个带有执行目标的线程对象
Thread(Runnable target,String name) 创建一个带有执行目标的 给定线程名称的线程对象
只有Thread类能够开启线程。
4.设置(获取)线程的名称
Thread(String name);//通过Thread类中的构造方法,对线程名称进行设置
setName(String name);//通过set方法
String name = getName();//获取线程名称
5.Thread类中常用的方法:
a > Thread 的构造方法
Thread() 空参 采用默认的起名字规则 Thread-0 开始
Thread(String name) 给线程起名字的构造
Thread(Runnable target) 创建一个带有执行目标的线程对象
Thread(Runnable target,String name) 创建一个带有执行目标的 给定线程名称的线程对象
b> 常用方法
String getName() 获取线程的名称
void setName(String name) 设置线程名称
void start() 开启新的线程
void run() 这个方法中定义线程要执行的任务,在run方法中代码都是线程需要执行的代码
c > 静态方法
获取正在执行的线程对象 谁执行当前代码 那个正在执行的线程对象就是谁
static Thread currentThread()
static void sleep(long time) 当前执行线程休息一会
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)
线程的实现总结:
第一种方式:继承Thread类
第一步:定义一个类去继承Thread类
第二步:重新run方法
第三步:将我们需要完成代码写入到run方法中
第四步:创建Thread子类对象
第五步:开启线程,调用start方法
第二种方式:实现Runnable接口
第一步:定义一个实现类,去实现Runnable接口
第二步:重新run方法
第三步:将我们需要完成代码写入到run方法中
第四步:创建Runnable实现类对象
Runable接口中并没有开启线程的方法,如果我们想开启线程,必须要借助于
Thread类。
第五步:将Runnable实现类对象,作为参数传递给Thread构造方法。
第六步:使用Thread类的start方法开启线程
6.两种实现线程的区别
类继承的特点:
单继承。如果一个类继承了Thread就不能再继承其他类。
接口的特点:
一个类可以实现多个接口,同时还可以继承一个类。
接口的出现就是为了打破类的单一继承性。
Thread 与 Runnable 区别
如果一个类 继承Thread,就不能再继承其他的类。 不适合资源共享
如果实现了 Runnable接口,并且可以再去继承其他的类。 可以实现资源共享
Runnable优势
1:适合多个相同的程序代码的线程 去共享同一资源
2:可以避免单继承的局限性
3: 增加了程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程是独立的。
4: 线程池只能放实现Runnable或Callable的线程,不能直接放继承Thread类
7.使用匿名内部类的方式 开启线程(了解)
什么是匿名内部类
本质
实现一个接口或者继承一个类 的 子类对象
格式
new 接口名/类名(){
重写方法
}
实现线程开启的方式 二
1:创建自定义类 Runnable的实现类
2:重写run方法
3:创建实现类对象
4:创建Thread对象 构造中传递 实现类对象
5:开启线程
划重点
new Thread(线程执行目标).start();
new Thread(Runnable的实现类对象).start();
8.java中同步机制:
当一个线程中的代码加上同步之后,这个线程执行不完,其他线程是无法进来执行的。
起到一个保护的作用,就是提高代码的安全性。
a> 同步代码块(静态代码块,局部代码块,构造代码块)
sychronized 关键字 可以用在某个方法中的某块区域
表示只对这个区域的资源实现互斥访问(同时只有一个线程进行操作,其他线程没有权限操作)。
格式
synchronized(锁对象){//锁对象,可以是任意对象
需要进行同步操作的代码
}
锁对象又称同步锁 只是一种概念 可以想象为在对象上标记了一个锁
1:锁对象可以是任意类型
2:多个线程 使用同一把锁
提示 在任何时候 最多允许一个线程拥有同步锁,其他线程只能在外面等待
如果多个线程使用同步代码块,要求这些线程的同步锁对象必须是同一个对象:
可以使用本类的字节码文件对象。
在成员的位置定义一个对象,使用static修饰
b> 同步方法:锁是默认的
同步方法 使用synchronized关键字修饰方法 就是同步方法
保证线程A在执行该方法的时候 其他线程只能等待
格式
public synchronized void method(){
可能出现线程安全问题的代码
}
同步锁是谁呢?
如果是 非静态方法 同步锁 就是this
如果是 静态方法 同步锁 当前方法所在类的字节码文件对象 (类名.class)
c> Lock锁
使用Lock锁 解决线程安全问题
JDK1.5 以后出现的
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作
更灵活
获取锁对象 : Lock lock = new ReentrantLock();
定义了两个方法
lock() 加锁
unlock() 释放锁
接口 java.util.concurrent.locks.Lock
实现类 ReentrantLock
9.线程的状态(JVM状态)
新建 -- 运行 -- 阻塞 -- 无限等待 -- 计时等待 -- 死亡
a> 计时等待
void sleep(long time);
让当前线程睡眠多长时间,
在同步中睡眠的时候不释放锁资源。
在非同步中和锁无关。
void wait(long timeout);//如果时间到了,会自动醒来
让当前线程等待多长时间,在等待的时候是会释放锁资源
b> 锁阻塞
c> 无限等待
void wait();//这种无限等待需要另外的线程来唤醒,如果不换醒那么永远处于等待状态。
Wating状态在API中介绍为:
一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态。
调用 wait()方法 可以让 正在执行的线程 进入 无限等待状态
调用 notify()方法 可以让 正在等待的线程 脱离无限等待状态
随机唤醒一个正在等待的线程
调用notifyAll() 可以让 所有正在等待的线程 脱离无限等待状态
唤醒所有正在等待的线程
这仨方法 来自Object
wait() 方法这里 有句话
当前线程必须拥有此对象监视器 (同步锁)
简而言之 wait()方法想要调用 有两个条件
1:必须在同步中
2:必须使用同步锁调用
notify()方法这里
唤醒在此对象监视器上等待的单个线程。
简而言之
1:必须在同步中
2: 必须使用同步锁调用
Thread.State getState()
返回该线程的状态。