2022-08-02到04 田龙跃 学习笔记

并发:在一”段“时间内,很多事情同时发生(程序开发角度:程序运行期间,多个线程同时操作相同资源)

线程:内存分给进程一部分计算资源,“进程的每个线程在线程被分配的内存中在争夺计算资源”(线程优先级是概率问题)

  • 创建线程的几种方式:
    1.继承thread类,重写run方法(这个类和方法都不是抽象的)继承后子类成为了一个独立的线程
    线程需要被启动(.start-是本地方法,启动"一个"线程(只有C语言可以操作CPU))调用run方法(直接调用run是普通的对象调方法)
    2.实现Runnable接口(函数式接口)重写run方法,但是要使用Thread构造一个线程才能使用.start
    3.(JDK1.5)实现callable接口<泛型接口> 重写 call() throws Exception 可以抛异常,有返回值,还要转成thread方法才能
    start(1.先转成runnable-使用futureTask构造成runnable2.转成thread类,使用thread构造)
    4.
  • 线程的生命周期:
    1.NEW 未被start调用执行(未加载)
    2.Runnable 线程正在JVM被执行,等待操作系统调度(加载后待运行)
    3Blocked 阻塞,因为某些原因不能立即执行(外部原因导致)
    (os:等待就是被挂起了,就不在cpu中了)
    4.Waiting 无限期等待(如果没被唤醒则一直等待)(等待一般是主动调用方法等待)
    5.TimedWaiting 有限期等待(等一段时间后自己进入)
    Terminated 终止线程的状态
  • 守护线程(守护用户线程的线程)(程序的小模块):为用户线程提供服务,用户需要时就出现,不需要就不用出现——经常用于支持后台任务(大多数JVM线程都是守护线程) 线程.setDaemon(true)设置守护线程
    *java内存模型:CPU-CPU寄存器-一级缓存-二级缓存-主内存 (线程和线程之间,共享数据的通信JAVA中都是必须要从主内存走一遍)
    (安全问题-读数据时和向主内存中写数据产生的问题)-程序就是数据的读写操作;
    1.(顺序性)指令重排(最大化的利用内存资源产生的结果):互相无关的指令之间计算机Cpu可以随机选择命令的执行顺序-或者说CPU用多个线程同时执行这些命令(volatile-内存屏障禁止重排序关键字可以解决)
    2.(可见性)不可见性:是指线程之间的可见性,一个线程修改的数据(没写到主内存之前)对另一个线程是不可见的(volatile关键字可以解决)volatile关键字可以保证直接从主存中读取一个变量,如果这个变量被修改后,总是会被写回到主存中去;
    3.(原子性)线程争抢:争抢计算资源,但都拿到了相同的资源进行操作(本来要操作两次,结果都对相同资源操作导致进行了同样的操作,相当于只操作了一次)加(synchronized同步)锁
  • 线程安全的实现方法:
    1.数据不可变:一切不可变的对象都是线程安全的 final;字符串
    2.互斥同步:加锁(悲观锁)
    3.非阻塞同步:{无锁变成}自旋
    4.无同步方案:限制共享数据的可见范围,就是共享(数据)所有的操作一个
    博文:https://zhuanlan.zhihu.com/p/29881777(谢谢作者大大)多看!!!!!!!
  • sychronized:拿到共享对象当成锁(只对于当前虚拟机可以解决线程安全问题-不能跨JVM解决问题)就是把同步的代码变成单线程执行
    同步方法
    1:修饰实例方法:调用该方法的实例(this)-加锁对象-同步监视器是实例对象
    2:修饰静态方法:类对象-加锁对象(同步代码块)实现runnable接口推荐使用
    同步代码块:obj(或类对象)-加锁对象(同步代码块)实现runnable接口推荐使用
  • 死锁:互相占着对方的锁不放手
    线程重入:拿到锁后不会再被同一个锁阻碍(synchronized可重入锁)—锁相当于认证
  • 为什么要使用wait(timed_wait)
    cpu会根据计算情况挂起或者唤醒一些线程(在内核中操作,系统压力大),此时应用上的共享数据会处于锁定状态,我们为了不让CPu进行挂起,所以我们人为让一些线程"等待"进入,让CPU能够一直保持运行状态(不进行挂起操作-耗内核)
  • 方法总结:
    1.sleep会释放cpu资源,但不会释放锁
    2.yield方法释放cpu执行权,但保留了CPU的执行资格
    3.join抢夺执行权
    4.wait释放资源,也释放锁
  • sleep和wait的区别(面试题)
    1.出处:join是Thread类提供的方法,wait是Object类提供的方法
    2.锁的控制:sleep会释放cpu资源,但不会释放锁;wait释放
  • 线程退出:推出标志
    1-.stop
    2-定一个volatile标记,通过标记来让线程确定是否执行
    3-.interrupt 调用此方法会抛异常,线程会进入挂起状态
  • ------------------------手动锁-实现synchroniazed---------------------------------能控制主动权
    1.LockSuport 锁工具类
    .park(阻塞线程)和.unpark(疏通线程)实现了wait和notify的功能:
    但park不释放锁(所以不会出现死锁);中断不会出现interruptedException
    2.Lock接口
    .lock当前线程获得锁 .trylock查看是否获得锁 .unlock当前线程释放锁
    reentrantLock-实现了lock接口
  • lock和synchronized的区别
    1.Lock是一个接口,synchronized是关键字,由c语言实现
    2.synchronized发生异常时会释放锁,lock发生异常时若没有主动释放,会一直占着锁,所以一般在finally中释放
    3.lock可以让等待锁的线程响应中断,使用synchronized只会让线程一直等待,不会中断
    4.lock效率比synchronized效率高
    lock子类—读写锁:ReentrantReadWriteLock读写锁保证了多个线程可以一起读数据,但是如果有个线程要写数据,其他线程就都不能读
    (再读操作多,写操作少时)内部类:readlock-writelock只要writelock启动了其他readlock就不会获得锁了
  • Atomic原子类,基本数据类型的包装类-保证我们的数据不出问题
  • 常见的工作队列:ArrayBlockingQueue:基于数组的有界阻塞队列
    LinkedBlockingQueue:基于链表的有界阻塞队列
  • 线程池的拒绝策略:AbortPolicy:直接抛出异常,默认的策略
    CallerRunPolicy:用调用者所在的线程执行任务
    DiscardOldestPolicy:丢弃阻塞队列中存在时间最长的线程,并执行当前任务
    DiscardPolicy:直接丢弃线程

Tips:对象监视器

点击查看多线程通信代码和注释
public class Test1 implements Runnable{

    public static  Object waitOBJ=new Object();

    @Override
    public void run() {
        synchronized (waitOBJ){
            System.out.println(Thread.currentThread().getName()+"拿到了锁");
            try {
                waitOBJ.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println(Thread.currentThread().getName()+"释放了锁");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(new Test1(),"线程一");
        thread.start();
//        System.out.println(Thread.currentThread().getName()+"正在运行");
        //让主线程睡了一会,不让主线程睡的话,主线程可能提前进行了唤醒操作,然后当我们线程进入等待的时候没人唤醒她就造成了思索
        Thread.sleep(1000);
//       必须是同一对象监视器‘waitOBJ’上的唤醒,等待操作才可以生效否则会报处非法监视器异常
        synchronized (waitOBJ){
            waitOBJ.notify();
//            证明了是主线程执行了唤醒操作
            System.out.println(Thread.currentThread().getName()+"正在运行");
        }
    }
}
点击查看多线程实现多窗口买票
package com.jr.Learn0813;

import org.testng.annotations.Test;

public class Test1 implements Runnable{
//  定义一个类的公共锁(static)
    public static  Object waitOBJ=new Object();
//    如果不加static每个线程就都有100张票
    public static  Integer num=100;

    @Override
    public void run() {
        while (true){
            synchronized (waitOBJ){
                System.out.println(Thread.currentThread().getName()+"->"+num--);
                try {
//                       如果数字小于等于三就说名任务该结束了
//                       因为我们有四个窗口,我们还要为其他还处在被挂起状态的线程留一个票,如果窗口更多需要留更多
                    if (num>3){//输出完4变成三了 3>2>1输出完就都退出了
//                        notifyAll只是又给了每个线程争抢的权力
                        waitOBJ.notifyAll();
                        waitOBJ.wait();
                    }else {
//                            唤醒了所有线程 os:一定要是同一个对象监视器
                        waitOBJ.notifyAll();
//                            退出循环结束了自己的任务(run方法)
                        break;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }

//        while (true){
//                synchronized (waitOBJ){
//                    System.out.println(Thread.currentThread().getName()+"->"+num--);
//                    try {
////                       如果数字小于等于三就说名任务该结束了
//                        if (num>3){//输出完4变成三了 3>2>1输出完就都退出了
//                        waitOBJ.notify();
//                        waitOBJ.wait();
//                        }else {
////                            唤醒了所有线程 os:一定要是同一个对象监视器
//                            waitOBJ.notifyAll();
////                            退出循环结束了自己的任务(run方法)
//                            break;
//                        }
//                    } catch (InterruptedException e) {
//                        throw new RuntimeException(e);
//                    }
//
//                }

//            System.out.println(Thread.currentThread().getName()+"释放了锁");
        }

    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(new Test1(),"线程一");
        thread.start();
        Thread thread1=new Thread(new Test1(),"线程二");
        thread1.start();
        Thread thread2=new Thread(new Test1(),"线程三");
        thread2.start();
        Thread thread3=new Thread(new Test1(),"线程四");
        thread3.start();
     }
}

posted @   沧月我爱  阅读(29)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示