zoukankan      html  css  js  c++  java
  • 多线程

    多线程

    进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。本质上就是一块内存空间。
    线程:就是进程中的程序执行的最小单元。

    一个进程至少有一个线程在运行,当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序,每个线程在栈区中都有自己的执行空间,自己的方法区、自己的变量。
    jvm在启动的时,首先有一个主线程,负责程序的执行,调用的是main函数。主线程执行的代码都在main方法中。

    当产生垃圾时,收垃圾的动作,是不需要主线程来完成,因为这样,会出现主线程中的代码执行会停止,会去运行垃圾回收器代码,效率较低,所以由单独一个线程来负责垃圾回收。(异常也是并行运行)


    随机性的原理:因为cpu的快速切换造成,哪个线程获取到了cpu的执行权,哪个线程就执行。

    1. 关于运行线程代码,要知道的是:
    • 返回当前线程的名称:Thread.currentThread().getName()
    • 线程的名称是由:Thread-编号定义的。编号从0开始。(当然可以自己去命名)
    • 线程要运行的代码都统一存放在了run方法中。

    • 线程要运行必须要通过类中指定的方法开启。start方法。

    3.线程状态:

    它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换

    1. 新建状态,当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值

    2. 就绪状态,当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行,

    • notify 是随机唤醒。 notify全部释放
    1. 运行状态,如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态

    2. 阻塞状态,当处于运行状态的线程失去所占用资源之后,便进入阻塞状态,有wait , sleep可以变成阻塞

    • wait 不会被被唤醒后是不会释放锁, sleep会释放锁
    1. 在线程的生命周期当中,线程的各种状态的转换过程!
    创建线程的第二种方式:实现一个接口Runnable

    1,定义类实现Runnable接口。
    2,覆盖接口中的run方法(用于封装线程要运行的代码)。
    3,通过Thread类创建线程对象;
    4,将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。
    为什么要传递呢?因为要让线程对象明确要运行的run方法所属的对象。
    5,调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法

    为什么要有Runnable接口的出现?

    1:通过继承Thread类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread类,因为java单继承的局限性。
      可是该类中的还有部分代码需要被多个线程同时执行。这时怎么办呢?
      只有对该类进行额外的功能扩展,java就提供了一个接口Runnable。这个接口中定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。
      所以,通常创建线程都用第二种方式。
    因为实现Runnable接口可以避免单继承的局限性。

    2:其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提。
      所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。
    实现Runnable接口可以避免单继承的局限性。而且,继承Thread,是可以对Thread类中的方法,进行子类复写的。但是不需要做这个复写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。所以Runnable接口将线程要执行的任务封装成了对象。

    wait(),notify(),notifyAll()用来操作线程为什么定义在Object类中,sleep 存在thread中?

    这些方法存在于同步中;
    使用这些方法必须标识同步所属的锁;
    锁可以是任意对象,所以任意对象调用方法一定定义在Object类中。
    而sleep休眠是需要设定固定时间而不是通过锁的方式。

    
    new Thread(new Runnable(){  //匿名
    public void run(){
    System.out.println("runnable run");
    }
    })
    {
    public void run(){
    System.out.println("subthread run");
    }
    }.start();  //结果:subthread run
    
    Try {
    Thread.sleep(10);
    }catch(InterruptedException e){}// 当刻意让线程稍微停一下,模拟cpu切换情况
    
    1. 多线程的安全问题:
      多线程环境下, 数据资源的争抢。
    2. 原因: 在某个时刻多条语句被一个线程执行的时候, 还没执行完, 就被其他的线程执行了。
    3. 解决方案: 同步代码块:
    synchronized(对象){
    	需要被同步的代码
    }
    

    同步

    好处:解决了线程安全的问题
    弊端:相对降低了性能, 因为判断锁需要消耗资源, 产生了死锁。

    1. 定义同步是有前提的:

    2. 必须是有两个以上的线程, 才需要同步

    3. 多个线程必须保证使用的是同一把锁

    4. 同步的第二种表现形式: 将同步关键字定义再函数上面, 让函数具备了同步性

    5. 同步函数用的是个锁, 函数都有自己所属的对象this, 所以同步函数所使用的锁就是本对象锁。

    6. 同步函数被static修饰的时候, 这个时候锁就是所属的类,也可以说是字节码文件对象, 也就是类名.class

    7. 同步代码块和同步函数的区别: 同步代码块使用的锁可以是任何对象

    8. 同步函数使用的锁是this, 静态同步函数的锁是字节码文件对象。再一个类中只有一个同步, 可以使用同步函数

    延迟加载的单例模式:
    class Single{
    private static Single s = null;
    private Single(){}
    public static Single getInstance(){ //锁是谁?字节码文件对象;
    if(s == null){
    synchronized(Single.class){
    if(s == null)
    s = new Single();
    }
    }
    return s;
    }
    }
    
    1. 同步死锁:将同步进行嵌套的时候就可以看见同步死锁
    2. 线程间的通信:思路:多个线程按照序列操作同一个资源,仓储模式
    • 将资源封装成对象
    • 将线程执行的任务(run)方法也封装成了对象
    1. 等待唤醒机制:
    • wait:将线程对象存储到线程池
    • notify:到线程池唤醒对象
    • notifyall: 唤醒线程池中所有的纤层
      注意点:
    • 这些方法都需要定义再同步中, 因为要标识所属的锁。
    • 者三个方法都定义再Object类中,因为三个方法都需要定义同步内,并标识同步锁, 而同步锁可以定义为任何对象, 所以这个对象最好的选择就是object
    wait和sleep区别:

    wait可以指定时间也可以不指定时间,会释放执行权
    sleep必须指定时间,不会释放执行权

    线程停止:

    1.通过定义循环的结束标记
    2. 通过interrupt中断线程

    关于优先级的问题:

    1. 设置优先级, 只会再大体上进行控制,就是说数量达到一定的程度的时候才可以。
    2. 而要真正的实现只有通过join和interrupt才可以终端线程, 而不是通过yield让步和设置优先级
    3. setDamon设置,将该线程标记为守护线程, 或者是用户线程
    4. toString可以返回线程的许多信息。 setProprity,getProprity.
    Lock接口:

    同步是隐示的锁操作,而Lock对象是显示的锁操作

    < java.util.concurrent.locks > Condition接口:await()、signal()、signalAll();
    --------------------------------------------------------
    class BoundedBuffer {
       final Lock lock = new ReentrantLock();
       final Condition notFull  = lock.newCondition();
       final Condition notEmpty = lock.newCondition();
       final Object[] items = new Object[100];
       int putptr, takeptr, count;
       public void put(Object x) throws InterruptedException {
         lock.lock();
         try {
           while (count == items.length)
             notFull.await();
           items[putptr] = x;
           if (++putptr == items.length) putptr = 0;
           ++count;
           notEmpty.signal();
         }
    finally {
           lock.unlock();
         }
       }
       public Object take() throws InterruptedException {
         lock.lock();
         try {
           while (count == 0)
             notEmpty.await();
           Object x = items[takeptr];
           if (++takeptr == items.length) takeptr = 0;
           --count;
           notFull.signal();
           return x;
         }
    finally {
           lock.unlock();
         }
       }
    }
    

    API:

    (Application Programming Interface,应用程序编程接口)就是让别人通过访问封装好的类, 而不用去理解底层直接调用即可。

  • 相关阅读:
    Understanding Bootstrap Of Oracle Database
    Oracle Null 与 in, exists 的关系说明(not in 查不到结果)
    Oracle Virtual Box 安装使用 说明
    PowerDesigner 企业架构模型 ( EAM ) 说明
    excel 数据导入 mysql
    Go语言基础之内置函数
    Go语言基础之defer语句
    匿名函数和闭包
    Go语言基础之类型别名和自定义类型
    【Github】remote: Support for password authentication was removed
  • 原文地址:https://www.cnblogs.com/jwlxtf/p/7932357.html
Copyright © 2011-2022 走看看