6_多线程

目录

多线程

在同一时刻,只能有一个程序在执行,感觉这些程序你是在同时进行,实际上是CPU在高效的切换。

进程

  • 概念

就是正在运行的程序,也就是代表了程序锁占用的内存区域。

  • 特点

  1. 独立性
    • 进程是系统中独立存在的实体,每个进程都有自己私有的地址空间。
    • 在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。
  2. 动态性
    • 进程与程序的区别在于程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。
    • 在进程中加入了时间的概念,进程具有自己的生命周期和各种不同的状态。
  3. 并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。

线程

  • 概念

  1. 线程(thread)是操作系统能够进行运算调试的最小单位。
  2. 它被包含在进程之中,是进程中的实际运作单位。一个进程可以开启多个线程。
  3. 一个程序运行后至少有一个进程,一个进程里包含多个线程。
    • 单线程:进程只有一个线程。
    • 多线程:进程中有多条执行路径。

多线程生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tpa59d3g-1609346543569)(D:\0000note\1基础note\img\多线程生命周期.png)]

线程生命周期,总共有五种状态:

  1. 新建状态:

    当线程对象创建后,即进入了新建状态

    Thread t = new MyThread();
    
  2. 就绪状态:(New)
    • 当调用线程对象的start()方法,线程即进入就绪状态。
    • 处于就绪状态的线程,只是说明此线程已经做好了被CPU调度执行的准备,并不会立即执行。
  3. 运行状态:(Runnable)
    • 当 CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行。
    • 就绪状态是进入到运行状态的唯一入口,也就是说线程要想进入运行状态,首先必须处于就绪状态中。
  4. 阻塞状态:(Blocked)
    • 处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。
    • 阻塞状态分三种:
      • 等待阻塞:运行状态中的线程执行wait() 方法,使本线程进入到等待阻塞状态。
      • 同步阻塞:线程在获取synchronixed同步锁失败(被其它线程占用),它会进入到同步阻塞状态。
      • 其他阻塞:
        • 通过调用线程的sleep() 或join()或发出了I/O请求时,线程会进入到阻塞状态。
        • 当sleep()状态超时、join()等待线程终止或超时、I/O处理完毕时线程重新转入就绪状态。
  5. 死亡状态:(Dead)
    • 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

多线程创建

继承Thread

  1. Thread本质上是实现了Runnable接口的一个实例,代表一个线程的实例。
  2. 启动线程的唯一方法就是通过Thread类的start() 实例方法。
  3. start()方法最终由操作系统启动一个新线程,操作系统将执行run() 方法。
常用方法
方法用处
String getName()返回该线程的名称
Static Thread currentThread()返回对当前正在执行的线程对象的引用
void setName(String name)改变线程名称
static void sleep(long millis)在指定的毫秒内让正在执行的线程休眠
void start ()使该线程开始执行
Tread(String name)分配新的Thread对象(构造方法)

实现Runnable接口

若当前类已经ectends另一个类,就无法多再继承Thread类,这里还可以实现Runnable接口。

常用方法
void run() 
//当实现接口的对象 Runnable被用来创建一个线程,启动线程使对象的 run在独立执行的线程中调用的方法。 

对比

方式优点缺点
Thread编写简单,定义了很多操纵线程的方法线程类已经继承了Thread类就不能再继承其他父类了。
Runnable线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。多个线程可以共享一个Target对象,很适合多个相同线程来处理同一份资源。编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread() 方法。
CallableRunnable规定的方法是run(); Callable规定的方法是call(); Callable的任务执行后可返回值,Runnable不行;call方法可以抛出异常,run方法不行存取其他项慢。
Pool线程池可以创建固定大小,这样无需反复创建线程对象,比较节省资源。编程繁琐,难以理解!

同步锁

  1. 把有可能出现问题的代码包起来,一次只让一个线程执行。通过sychronized关键字实现同步。
  2. 当多个对象操作共享数据时,可以使用同步锁解决线程安全问题。
synchronized

两种用法:

  1. 代码块锁

    synchronized(对象){
        需要同步的代码;
    }
    
  2. 实例锁:

    • 修饰普通方法,在方法返回值前加synchronized。(默认是synchronized(this))

    • 修饰static,在方法返回值前加synchronized。(默认是synchronized(className.class))

    • 当共享资源是static类型、实例锁修饰的方法是非静态方法时,实例锁此锁无效。要用代码块锁(synchronized(className.class))

      class A extends Thread{
          static private int i;
          public void run(){
         		synchronized(A.class){
                  sout(i);
         		}
         	}
      }
      
特点
  1. 前提
    • 同步需要两个或者两个以上的线程。
    • 多个线程间必须使用同一个锁。
  2. 同步的缺点是会降低程序的执行效率,为了保证线程安全,必须牺牲性能。
  3. 可以修饰方法称为同步方法,使用的锁对象是this。
  4. 可以修饰代码块称为同步代码块,锁对象可以任意。

单例设计模式(Singleton Pattern)

  1. 单例模式属于创建型模式,提供了一种创建对象的最佳方式。spring默认创建的bean就是单例模式的。
  2. 单例模式确保对象只有一个,就是类在内存中的对象只有一个。
    • 单例类只能有一个实例。
    • 单例类必须自己创建自己的唯一实例。
    • 单例类必须给所有其他对象提供这一实例。
介绍
  • 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

  • 一个全局使用的类频繁地创建与销毁。

##### 处理思路

1. 判断系统是否已经有这个单例,有则返回,没有则创建。
2. 关键代码——私有化构造函数。

##### 优点

1. 内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
2. 避免对资源的多重占用。

##### 缺点

​	没有接口,不能继承,与单一职责原则冲突(一个类应当只关心内部的逻辑,而不关心外面怎么实例化)。
实现——懒汉式(线程不安全)
描述
  1. 这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。
  2. 因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
    这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
实例
public class MySingle{
    private static MySingle my;
    private MySingle(){}
    public static MySingle getMy(){
        if(my == null) my = new MySingle();
        return my;
    }
}
实现——懒汉式(线程安全)
描述(延迟加载的思想)
  1. 这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
  2. 优点:第一次调用才初始化,避免内存浪费。
  3. 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
    getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。
public class MySingle{
    private static MySingle my;
    private MySingle(){}
    public static synchronized MySingle getMy(){
        if(my == null) my = new MySingle();
        return my;
    }
}
实现——饿汉式(线程安全)
描述(延迟加载的思想)
  1. 这种方式比较常用,但容易产生垃圾对象。

  2. 优点:没有加锁,执行效率会提高。

  3. 缺点:类加载时就初始化,浪费内存。

  4. 它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

public class MySingle{
    //创建自身对象
    private static MySingle my = new MySingle();
    //私有化构造方法
    private MySingle(){}
    //设置全局访问方法
    public static MySingle getMy(){
        return my;
    }   
    
    public void method(){
		System.out.pringt("Hello World!");
    }
}

public class Demo{
    public static void main(String [] args){
        MySingle my = MySingle.getMy();
        my.method();//输出:Hello World!
    }
}
实现——双检锁/双重校验锁(DCL)(延迟加载,线程安全)
描述

​ 这种方式采用双锁机制,安全且在多线程的情况下能保持高性能。

public class MySingle{
    
    private volatile static MySingle my;
    
    private MySingle(){}
    
    public static MySingle getMy(){
        if(my == null){
            synchronized(MySingle.class){
                if(my == null){
                    my = new MySingle;
                }
            }
        }
        return my;
    }
    
}

锁分类

死锁\活锁

公平锁\非公平锁

互斥锁\自旋锁

可重入锁

可中断锁

读写锁

乐观锁\悲观锁

偏向锁\轻量级锁\重量级锁

线程阻塞唤醒

  1. suspend 和resume
  2. wait 和 notify
  3. await 和 singal
  4. park 和 unpark

等待队列

锁池

线程池

锁的获取和释放

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值