Java高级_Day15(多线程)
多线程
基本概念:
程序:
- 是完成特定任务,使用编程语言编写的一组计算机指令的集合,是静态的。
进程:
- 是程序的执行的过程,是动态的。是程序运行的过程。包括启动、运行、消亡的生命周期
线程:
- 是程序执行的一条路径。
多线程就是在程序执行的过程中的同一个进程在同一个时间执行多个线程。
线程是执行和调度的基本单位,每个线程都用友独立的运行栈和程序计数器,线程之间可以并行执行,线程之间可以进行相互切换,线程间的切换的开销比较小
在一个进程中的线程,可以共享进程的内存,共享数据。可以访问相同的变量和对象。在多线程的执行中,可以提高计算机的运行的效率和程序执行的速度,但是由此也会产生线程间的安全问题。
单核CPU:
- CPU 中只有一个可执行单元, 在同一时刻,只能执行一个线程任务。
多核CPU:
- CPU中有多个可执行单元 在同一时刻可以执行多个线程任务
并行:
- 多个CPU同时执行多个任务
并发:
- 一个CPU同时执行多个任务
CPU的执行机制:
- 抢占式
- 轮询式
多线程的优点:
- 提高程序的相应的速度,提升用户体验
- 提高CPU的使用率
- 改善程序的结构。让程序结构更加的合理,可以独立运行。
多线程的实现
Java 支持多线程编程。多线程的实现是通过Thread类来实现
Thread:
- 线程实现需要依赖于Thread类
- 对于线程的任务的完成,需要通过run()方法来实现,是线程的主体
- start() 是线程的启动方法。启动之后会自动调用线程的run方法
对象的创建:
Thread()
分配一个新的 Thread对象。
Thread(Runnable target)
分配一个新的 Thread对象。
Thread(Runnable target, String name)
分配一个新的 Thread对象。
Thread(String name)
分配一个新的 Thread对象。
具体的实现:
public class MyThread extends Thread{
//设置一个字符串,以便于区分
private String str;
public MyThread() {
}
public MyThread(String str) {
this.str = str;
}
// 要使得一个类成为多线程的类则需要继承Thread
//必须要重写run方法,run方法将是该线程所需要完成的任务
@Override
public void run() {
for(int i = 0; i < 100;i++){
System.out.println(str + i);
}
}
}
public static void main(String[] args) {
//创建线程对象
MyThread mt1 = new MyThread("--------");
MyThread mt2 = new MyThread("+");
//启动线程
mt1.start();
mt2.start();
}
结果:
- 为什么重写run()方法:
run方法是线程的核心方法 是封装了线程的核心任务。- run和start的区别:
run方法是线程执行的核心任务的代码的封装而来的方法。使用中,我们自己不能去调用run 如果自己调用run方法 那么此时的run方法就是一个普通方法,与多线程无关
start方法启动线程,线程自己回去调用run(run方法是由jvm来调用)
实现二(内部类):
ublic static void main(String[] args) {
//使用内部类实现Thread
Thread t1 = new Thread(){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
System.out.println(getName() + i);
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
System.out.println(getName() + i);
}
}
};
//启动线程
t1.start();
t2.start();
}
public static void main(String[] args) {
//匿名类
new Thread(){
@Override
public void run() {
for(int i = 0; i < 100 ; i++){
System.out.println("-----" + i);
}
}
}.start();
new Thread(){
@Override
public void run() {
for(int i = 0; i < 100 ; i++){
System.out.println("+" + i);
}
}
}.start();
当程序中存在多个线程的时候,线程间的执行采用的是CPU的执行机制。多个线程之间执行会呈现一种交叉执行的现象。
线程的基本属性:
返回值类型 | 方法 |
---|---|
void | setName(String name) 将此线程的名称更改为等于参数 name 。 |
String | getName() 返回此线程的名称。 |
long | getId() 返回此线程的标识符。 |
Thread | currentThread() 返回对当前正在执行的线程对象的引用。 |
public static void main(String[] args) {
//创建线程
Thread t1 = new Thread(){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
System.out.println(getName() + i);
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
System.out.println(getName() + i);
}
}
};
//为线程设置名字
t1.setName("第一个线程");
t2.setName("第二个线程");
//获取名字
System.out.println(t1.getName());
System.out.println(t2.getName());
//获取标识
System.out.println(t1.getId());
System.out.println(t2.getId());
//获取当前线程
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
t1.start();
t2.start();
}
结果:
线程的优先级
返回值类型 | 方法 |
---|---|
static int | MAX_PRIORITY 线程可以拥有的最大优先级。 |
static int | MIN_PRIORITY 线程可以拥有的最小优先级。 |
static int | NORM_PRIORITY 分配给线程的默认优先级。 |
int | getPriority() 返回此线程的优先级。 |
void | setPriority(int newPriority) 更改此线程的优先级。 |
线程优先级的使用:.
public static void main(String[] args) {
Thread t1 = new Thread("线程1"){
@Override
public void run() {
for(int i = 0 ; i < 1000 ; i++){
System.out.println(Thread.currentThread().getName() +"----"+ i );
}
}
};
Thread t2 = new Thread("线程2"){
@Override
public void run() {
for(int i = 0 ; i < 100000 ; i++){
System.out.println(Thread.currentThread().getName() +"----"+ i );
}
}
};
Thread t3 = new Thread("线程3"){
@Override
public void run() {
for(int i = 0 ; i < 10000 ; i++){
System.out.println(Thread.currentThread().getName() +"----"+ i );
}
}
};
//获取线程的默认优先级 线程的默认优先级相同都是5
System.out.println(t1.getPriority());//5
System.out.println(t2.getPriority());//5
System.out.println(t3.getPriority());//5
//线程的优先级的范围 10 -- 1
System.out.println(Thread.MAX_PRIORITY);// 10
System.out.println(Thread.MIN_PRIORITY);// 1
System.out.println(Thread.NORM_PRIORITY);// 5
//线程优先级的设置 线程优先级对线程执行的影响
t1.setPriority(10);
t2.setPriority(1);
t3.setPriority(5);
//启动线程
t1.start();
t2.start();
t3.start();
}
线程的优先级高,意味着线程获得CPU的执行权的几率大;线程优先级小,意味着线程获得CPU的执行权的几率小
线程的调度方式:
分时调度:
- 线程之间轮流使用CPU,平均分配给每个线程占用CPU的时间
抢占调度: - 对于线程获取CPU的执行权,可以通过线程的优先级来改变。优先级高的线程,将拥有优先获得CPU的执行权,如果线程的优先级相同,那么此时线程获得CPU的执行权是随机的。优先级高,指的是线程获取CPU的执行权的几率更大。
线程的控制:
返回值类型 | 方法 |
---|---|
static void | sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 |
void | join() 等待这个线程死亡。 |
void | join(long millis) 等待这个线程死亡最多 millis毫秒。 |
static void | yield() 对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。 |
slepp 线程休眠:
public static void main(String[] args) {
Thread t1 = new Thread("线程1"){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
System.out.println(getName() + "-" + i);
}
}
};
Thread t2 = new Thread("线程2"){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
if(i == 50){
System.out.println(getName() + "-------------------------" + i);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
System.out.println(getName() + "--" + i);
}
}
}
};
Thread t3 = new Thread("线程3"){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
System.out.println(getName() + "---" + i);
}
}
};
//启动线程
t1.start();
t2.start();
t3.start();
}
join 线程加入:
public static void main(String[] args) {
Thread t1 = new Thread("线程1"){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
System.out.println(getName() + "-" + i);
}
}
};
Thread t2 = new Thread("线程2"){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
if(i == 50){
try {
System.out.println(getName() + "-------------------------" + i);
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
System.out.println(getName() + "--" + i);
}
}
}
};
Thread t3 = new Thread("线程3"){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
System.out.println(getName() + "---" + i);
}
}
};
t2.setPriority(10);
t1.start();
t2.start();
t3.start();
}
当某个线程执行中调用了其他线程的join方法时,调用线程将被阻塞,直到join方法加入的join线程执行完位为止
yield 线程让步(礼让线程):
public static void main(String[] args) {
Thread t1 = new Thread("线程1"){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
System.out.println(getName() + "-" + i);
}
}
};
Thread t2 = new Thread("线程2"){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
if(i == 50){
System.out.println(getName() + "-------------------------" + i);
Thread.yield();
}
else{
System.out.println(getName() + "--" + i);
}
}
}
};
Thread t3 = new Thread("线程3"){
@Override
public void run() {
for(int i = 0;i < 100 ; i ++){
System.out.println(getName() + "---" + i);
}
}
};
//启动线程
t1.start( );
t2.start();
t3.start();
}
暂停当前正在执行的线程,把执行的机会让给优先级相同的或者更高级别的线程
如果在线程中,没有同优先级的线程,则忽略此方法
当在某一个线程中调用了yield,并不意味着当前线程一定会失去CPU的执行权,只是此时其他同级别或高级别的线程将拥有了获取CPU执行权的机会。
线程的分类:
Java中线程的分类:守护线程和用户线程
守护线程守护的是用户线程,当用户线程执行结束,守护线程无论是否执行完毕,都将被迫结束。
public static void main(String[] args) {
Thread td1 = new ThreadDaemon();
Thread td2 = new ThreadDaemon();
td1.setName("守护线程1");
td2.setName("守护线程2");
//设置td1 和td2 为守护线程
td1.setDaemon(true);
td2.setDaemon(true);
td1.start();
td2.start();
new Thread("用户线程1"){
@Override
public void run() {
for(int i = 0 ; i < 10;i++){
System.out.println(Thread.currentThread().getName()+"******" + i);
}
}
}.start();
new Thread("用户线程2"){
@Override
public void run() {
for(int i = 0 ; i < 10;i++){
System.out.println(Thread.currentThread().getName()+"******" + i);
}
}
}.start();
}
线程的生命周期:
线程的生命周期的表示是通过线程的几种状态来进行表示。
线程的状态:
- 新建:当一个Thread类或者他的子类对象被创建时,此时的线程就处于新建状态
- 就绪:处于新建状态的线程被start后,线程就进入到CPU的执行队列,此时的线程将时刻准备着去争抢CPU的执行权,线程已经具备了运行的条件,只是还没有得到CPU的资源。
- 运行:处于就绪状态的线程获得了CPU的执行权,那么此时的线程就进入了运行状态,将会执行run方法
- 阻塞:在运行状态的线程,因为某些原因暂时失去了CPU的执行权,线程被挂起,临时出让了CPU的执行权,终止了当前执行
- 死亡:线程完成了全部的任务或线程被强制终止执行或者是因为线程在执行过程中出现了异常