0
点赞
收藏
分享

微信扫一扫

多线程进阶

进程:

正在运行的程序的实例(正在进行的程序)

指运行中的程序,只要运行起来就会占用内存空间
比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间,当我们使用微信,又启动了一个进程,操作系统将为微信分配新的内存空间

线程:

是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。(每个正在进行的程序干了多件事,每个独立的流程叫线程)
(微信可以接受消息,也可以发送消息/打开main方法,可以执行多个方法)

线程是由进程创建的,是进程的一个实体
一个进程可以有多个线程
比如打开百度云(打开进程),下载任务,任务1(线程1),任务2(线程2)。

多线程 执行原理 ( 并发执行原理 )

CPU核心: 每一个计算机中CPU核心, 就相当于人的一个大脑, 可以执行一件事情.

时间片: 将CPU未来可以用于执行的时间 ,碎片化, 拆分成一个个的小的时间片段. 每一个时间片段的大小 可以都不足一毫秒.

程序在争抢到时间片段后, 会进行执行 .

Java实现线程的两种方式

  1. 继承Thread类,重写run方法
    当Thread调用start方法时,本地方法start0会通过C和C++代码开启新的执行路径(线程)
    新线程会将Thread的run方法加载进线程执行
            MyThread1 t1 = new MyThread1();
            MyThread2 t2 = new MyThread2();
            t1.start();
            t2.start();
    
    
        }
        static class MyThread1 extends Thread{
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("甲:"+i);
                }
            }
        }
        static class MyThread2 extends Thread{
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("乙:"+i);
                }
            }

            

  2. 编写一个Runnable(接口,抽象方法run)的实现类,并创建对象
            MyRunnable1 m1 = new MyRunnable1();
            Thread t1 = new Thread(m1);
            MyRunnable2 m2 = new MyRunnable2();
            Thread t2 = new Thread(m2);
    
            t1.start();
            t2.start();
    
    
        }
        static class MyRunnable1 implements Runnable{
    
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("甲:"+i);
                }
            }
        }
    
        static class MyRunnable2 implements Runnable{
    
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("乙:"+i);
                }
            }
        }

  3. 方式1的简写,直接创建匿名内部类
            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();

  4. 方式2的简写
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        System.out.println("甲:"+i);
                    }
                }
            });
    
    
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        System.out.println("乙:"+i);
                    }
                }
            });
            t1.start();
            t2.start();

  5. 用第一种还是第二种?
    效率其实是一样的
    第一种继承Thread类,假设还需要继承别的类,Java是单继承,导致无法再继承别的类,但是第二种接口是允许多实现的,第二种更加灵活。
  6. Thread常用方法

面试题:调用start()与调用run()的区别在哪?

start():-->start0-->C代码-->创建线程-->在新线程加载run方法,执行
         当Thread调用start方法时,本地方法start0会通过C和C++代码开启新的执行路径(线程),           新线程会将Thread的run方法加载进线程执行
run():run 执行当前的线程

面试题:Java中存在单线程程序吗?

不存在。Main和GC线程,是开始到结束始终存在的

Thread类的常用静态方法

sleep()     休眠
currentThread()      Thread.currentThread().getName()获取线程名字yield()   此方法执行时所在的线程, 让出当前时间片. 用于降低线程的活跃度

Thread类的常用非静态方法

start() 启动此线程所表示的线程对象,在新的线程中执行run方法
stop()停止线程,已过时
interrupt()中断线程,此方法用于在某一线程上加入中断标记

守护线程
线程分为两类:用户线程+守护线程。当一个软件不存在任何用户线程时,守护线程自动死亡

用户线程

通常情况下, 我们自己开启的线程都是用户线程, 所有的用户线程都是一条单独的执行路径, 一个软件的所有用户线程执行完毕, 程序会死亡 .

守护线程

程序中存在一种特殊的线程, 叫做守护线程, 可以理解为 : 守护用户线程 .

当程序中所有的用户线程都死亡了. 守护线程无论是否执行完毕, 都会自动死亡.

Java中的垃圾回收器 GC 其实就是守护线程.


.setDaemon(true)  随着死亡

线程优先级

.setPriority()

A方法在新的线程中执行,A中调用其他方法,调用的方法也一定在新的线程中执行

线程的生命周期

 一个线程被创建以后,进入就绪状态,开始抢时间片去执行,执行完毕死亡。中间遇到特殊状态会等在那,有可能休眠一段时间,有可能是在读取一个文件,有可能是在做一些别的事。

1.创建

Plain Text

public class MyThread extends Thread{

@Override

public void run() {

//...

}

}

//新建就是new出对象

MyThread thread = new MyThread();

当程序使用new关键字创建了一个线程之后,该线程就处于一个新建状态(初始状态),此时它和其他Java对象一样,仅仅由Java虚拟机为其分配了内存,并初始化了其成员变量值。此时的线程对象没有表现出任何线程的动态特征,程序也不会执行线程的线程执行体。

2.就绪

当线程对象调用了Thread.start()方法之后,该线程处于就绪状态。

Java虚拟机会为其创建方法调用栈和程序计数器,处于这个状态的线程并没有开始运行,它只是表示该线程可以运行了。从start()源码中看出,start后添加到了线程列表中,接着在native层添加到VM中,至于该线程何时开始运行,取决于JVM里线程调度器的调度(如果OS调度选中了,就会进入到运行状态)。

3.运行

当线程对象调用了Thread.start()方法之后,该线程处于就绪状态。

添加到了线程列表中,如果OS调度选中了,就会进入到运行状态

4.阻塞

阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况大概三种:

  • 1、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
  • 2、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
  • 3、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)。
  • 线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。
  • 线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。唤醒线程后,就转为就绪(Runnable)状态。
  • 线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
  • 线程加入:join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
  • 线程I/O:线程执行某些IO操作,因为等待相关的资源而进入了阻塞状态。比如说监听system.in,但是尚且没有收到键盘的输入,则进入阻塞状态。
  • 线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意性的,并在对实现做出决定时发生。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。

5.死亡

线程会以以下三种方式之一结束,结束后就处于死亡状态:

  • run()方法执行完成,线程正常结束。
  • 线程抛出一个未捕获的Exception或Error。
  • 直接调用该线程的stop()方法来结束该线程——该方法会产生固有的不安全性,不推荐.

线程同步(线程安全问题  临界资源出错问题)

同步即排队,异步即并行

线程同步的两种实现方式:同步方法+同步代码块
两种方式都是通过加锁来实现的
 

同步代码块

格式:

synchronized(Object 锁对象){
}

在同步代码块时 我们手动指定锁对象时 . 多个线程必须使用同一个锁 ,才可以完成同步操作 !
static Object o = new Object();

同步方法

非静态的同步方法的锁对象为this  看是否同一个对象

静态方法的锁对象 , 是我们的类信息对象 (类名.class).    启动类.class

格式

权限修饰符 synchronized 返回值声明 方法名(形式参数列表){

}

举报

相关推荐

0 条评论