17.线程与进程

  1. 什么是进程
    1- 应用程序执行的实例
    2- 有独门的内存空间,占用一定的系统资源
    3- 图解:office进程等,

  2. 什么是线程
    1- 进程中执行运算的最小单位(cpu调试分配的最小单位)
    2- 对进程的开发实质上就是对线程的开发
  3. 进程与线程的关系
    1- 后者所属前者
    2- 一个进程最少由一个线程组成
  4. 什么是并行
    1- 多个cpu实例或多台机器同时执行一段处理逻辑
    2- 举例
    我操作电脑跟一个网友聊天的同时还在操作手机给另一个网友聊天(我有多个QQ)
  5. 什么是并发
    1- 通过cpu调度算法,让用户看上去是同时执行,实际是不是
    2- 举例
    我用一台电脑先给甲发消息,然后立刻给给已发消息,然后再给甲发消息
  6. 并发与并行图解(parallel,并发;concurrent,并行)
    1-

  7. 线程安全
    1- 它用于描述一段代码的可靠性
    2- 在并发时,调度情况不同,如果执行的结果不相同,此代码是非线程安全的
    3- 在使用共享资源时,这问题特别突出:可以使用同步技术解决这个问题
  8. 什么是多线程
    1- 一个进程中可运行了多个线程,用来完成不同的任务
    2- 多线程只是交替的抢占cpu资源,并非并行执行
  9. 多线程的作用
    1- 充分利用硬件资源:比如迅雷可同时下载多个文件,充分利用网络资源,cpu读写能力
    2- 简化编程模型,提高用户体验
  10. java如何进行线程开发
    1- 支持线程的API
    1- java.lang.Thread类
    2- java.lang.Runnable接口
    2- 主线程
    1- main()方法是主线程的入口
    2- 它是产生其它线程的线程
    3- 如何使用API创建线程
    1- 继承Thread类,重写public void run()方法
    2- 实现Runnable接口,重写 public void run()方法
    3- 说明
    1) 前者的run()方法,实质是调用子类重写的run方法,但是它是线程实现的关键类,如下源码
    2) 后者run()方法只是定义了这种行为,没有实质的意义
    3) 比较两种创建方式的优缺点(建议使用后者,程序易于扩展)
    1. 前者
    1. 缺点:如果已经有了父类,就不能用这种方法
    2. 优点:可以直接使用Thread 类中的方法,代码简单
    2. 后者
    1. 缺点:不能直接使用Thread 中的方法需要先获取到线程对象后,才能得到Thread 的方法,代码复杂
    2. 优点:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
    4- 如何启用创建好的线程
    1- 对于继承Thread类的方式创建的线程,直接实例化后调用 start()方法即可
    2- 对于实现Runnable接口方式创建的线程,需要使用它实例当作Thread构造实例的参数来创建线程对象
    3- 图解

    4- 说明
         程序的执行顺序取决于编码逻辑顺序,而线程要例外考虑,谁先抢占到系统资源谁就执行(由系统决定),,当然影响因素最大的是线程优先级
             先启动A线程,后启动B线程:B不一定比A后执行
             A线程的优先级高于B:B也不一定比A后执行,只是A线程先执行的概率要高些
             图解
    
    
         多个线程交替执行,不是真正的并行
         线程的每次执行时长由分配的cpu时间片长度决定
          run方法与start方法的区别
             run仅是普通方法,没有开启线程;
             start方法不仅启用了线程,而且调用了run方法
             图解

    5- 线程的状态及它们的相互转换
    1- 状态
    创建:new
    就绪:Runnable
    阻塞:Blocked
    运行:Running
    死亡:Dead

    2- 转换图解
    
    
    
         说明
             线程在Running的过程中可能会遇到阻塞(Blocked)情况
             调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度
             调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)
             对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)
             在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable

    6- 线程调度
    1- 按照特定的机制为线程分配cpu使用权
    2- 常用方法
    setPriority(int newPriority) 理发线程的优先级
    由1-10表示,1最低,默认优先级是5
    优先级越高获取cpu资源的概率越大 最高优先级字段:MAX_PRIORITY
    最低优先级字段:MIN_PRIORITY
    static void sleep(long mills) 在指定的毫秒数内让当前正在执行的线程休眠
    void join() 强制执行某线程:使用当前线程停止执行,其它线程结束后再继续执行本线程
    static void yield() 让别的线程执行:暂停当前执行的线程
    void interrupt() 中断状态置位,设置true,并非停止当前线程运行
    boolean isAlive() 测试线程是否处于活动状态
    getName() 获取线程的名称
    setName() 设置线程的名称
    Thread(Runnalbe r,名称) 可根据名称创建线程对象
    Thread.currentThread() 获取当前线程对象
    3- 深入理解中断操作
    之前更加直接的做法如下(当然后面被标记为deprecate,废弃的方法:不合理,比如持有的琐永远休眠)
    停止线程的运行:调用stop()方法
    延迟线程的运行,然后根据需要恢复线程的运行:suspend()与resume()
    既然不能直接杀掉一个线程,但是实质中需要把它杀掉,如何做呢?(告诉他,你应该自行了绝了,让他自我处理)
    使用等待通知机制
    给那个线程一个中断信号
    使用情况下使用中断操作
    子线程为了等待某个特定条件到来,先调用了Threa.sleep(3000),预期3秒后醒来,但是特定条件提前到来,需要子线程立即运行:此时就需要中断子线程的sleep状态
    主线程通过调用join来强制执行子线程,但是主线程没法等到子线程执行完毕后执行:此时就需要中断子线程的执行状态
    上面两种情况都是用于线程阻塞时中断,当然线程在非阻塞时也可中断
    前者
    一般是线程调用(sleep(),wait(),join())方法中的一种
    然后调用:Thread.interrupt()方法,这个线程收到中断信号
    然后会抛出InterruptedException,同时把中断状态设置为true
    后者
    只是把中断状态设置为true
    为什么阻塞线程中断时会抛出异常
    没有占用cpu运行的线程是不可能给自己中断状态置位的,这就会产生一个InterruptedException
    中断操作并没有停止线程运行,它的意义是什么
    用于中断线程的某一部分业务逻辑,前提是要检查自己的中断状态
    中断线程编码应用
    中断非阻塞线程
    举例
    //Interrupted的经典使用代码
    public void run(){
    try{
    ....
    while(!Thread.currentThread().isInterrupted()&& more work to do){
    // do more work;
    }
    }catch(InterruptedException e){
    // thread was interrupted during sleep or wait
    }
    finally{
    // cleanup, if required
    }
    }
    图解

                 中断阻塞线程
                     外部定义一个状态volatile修饰的变量
                     run方法中使用此变量梁用操作条件
                         没中断时执行的操作
                             try语句块中是阻塞操作
                             catch语句块中是真正操作
                         中断时执行的操作
                     图解

    7- 线程安全解决方法:多个线程操作同一共享资源时,将引发数据不安全问题
    1. 图解

    2. 使用同步方法或同步代码块解决数据不安全问题
         同步方法
             使用synchronized修饰的方法控制对类成员变量的访问
    
             synchronized就是为当前的线程声明一个锁
         同步代码块
             使用synchronized关键字修饰的代码块
    
             syncObject为需同步的对象,通常为this,效果与同步方法相同
             多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
             对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
         图解
    
    3. 说明
         同一时刻只能有一个线程进入synchronized(this)同步代码块
         当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
         当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
         多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁(尽量不要嵌套使用)

    每个对象都有的同步工具

    1. 工具有:synchronized,wait,notify,notifyAll
    2. java中每个对象都有现代战争监视器,来监测并发代码的重入在非多线程编码时该监视器不起作用,反之在synchronized范围内,监视器发挥作用
    3. wait/notify必须存在于synchronized块中,并且这三个关键字针对的是同一监视器,意味着wait后,其它线程可进入同步块执行
    4. 当某代码不具有监视器的使用权,即脱离同步代码块,去wait或notify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。
    5. synchronize单独使用
    
    6. synchronized, wait, notify结合:典型场景生产者消费者问题
         https://blog.csdn.net/csxiaoyuhan/article/details/78480980

    8- volatile关键字的使用
    1. 多线程的内存模型:main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)
    2.
    3. 针对多线程使用的变量如果不是volatile或者final修饰的,很有可能产生不可预知的结果(另一个线程修改了这个值,但是之后在某线程看到的是修改之前的值)。其实道理上讲同一实例的同一属性本身只有一个副本。但是多线程是会缓存值的,本质上,volatile就是不去缓存,直接取值。在线程安全的情况下加volatile会牺牲性能。
    9- java线程之间的通信(等待/通知机制)
    1. 等待通知机制就是用来完成多个线程之间通信的
    线程开始运行,拥有自己的栈空间,就如同一个脚本一样,按照代码一步步的执行直到终止。
    但是,每个运行中的线程,如果仅仅是孤立地运行,那么没有太大的价值
    2. 一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作
    整个过程开始于一个线程,而最终执行又是另一个线程。
    前者是生产者,后者是消费者
    实现这种机制最简单的方法就是让消费者线程不断的循环检查变量是否符合预期,如下代码所示
    while(value!=desire){
    xxx.wait();
    }
    doSomething();
    说明
    在while循环中设置不满足条件的条件,如果条件满足则退出while循环,从而完成消费者的工作
    让消费者线程wait的作用是让其进入WAITING状态,当生产者线程更新的value值的时候就进行notify唤醒该线程,让消费者线程完成doSomething.
    3. 完整代码

    4. 说明
         调用 wait(),notify(),notifyAll()时需要先对调用对象加锁。
         调用wait()方法后,线程由RUNNING变为WAITING,并将当前线程放置于对象等待队列
         notify(),notifyAll()方法被调用后,等待线程依然不会从wait()方法返回,而是等调用notify(),notifyAll()的线程释放该锁之后,等待线程才有机会从wait()返回
         notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而 notifyAll()方法则是把所有等待线程从等待队列中移到同步队列中,被移动的线程的状态由WAITING变成BLOCKED
         等待/通知机制依托于同步机制,其目的就是确保等待线程从wait()方法返回时能够感知到通知线程对变量的修改。
    5. 等待/通知的经典范式
         等待方的原则
             获取对象的锁
             如果条件不满足,那么调用锁的wait()方法,使该线程进入waiting,被通知后依然要检查条件
             条件满足则执行对应的逻辑
             伪代码
                  synchronized(对象){
                    while(条件不满足){
                     对象.wait();
                  }
                  对应的逻辑处理
                }
         通知方的原则
             获取对象的锁
             改变条件
             通知所有等待在该对象上的线程
             伪代码
                  synchronized(对象){
                     改变条件
                     对象.notifyAll();
                }

    10- 线程组
    1. Java 中使用ThreadGroup 来表示线程组,它可以对一批线程进行分类管理,Java 允许程序直接对线程组进行控制
    2. 默认情况下,所有的线程都属于主线程组
    3. 常用方法
    public final ThreadGroup getThreadGroup()//通过线程对象获取他所属于的组
    public final String getName()//通过线程组对象获取他组的名字
    ThreadGroup(String name) 创建线程组对象并给其赋值名
    Thread(ThreadGroup?group, Runnable?target,String?name)//创建线程组,并把线程添加到该组中,并且给线程命名
    setDaemon(true);//把线程组设置为守护线程组
    11- 线程池
    1. 程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互
    2. 而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池
    1. 线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用
    2. 在JDK5 之前,我们必须手动实现自己的线程池,从JDK5 开始,Java 内置支持线程池
    3. Executors 工厂类来产生线程池,有如下几个方法
    1. public static ExecutorService newFixedThreadPool(int Threads)
    2. public static ExecutorService newSingleThreadExecutor()
    3. 说明
    1. 这些方法的返回值是ExecutorService 对象,该对象表示一个线程池,可以执行Runnable 对象或者Callable 对象代表的线程。
    2. 它提供了如下方法
    1. Future<?> submit(Runnable task)
    2.

猜你喜欢

转载自www.cnblogs.com/River111/p/9716599.html