线程基础、线程之间的协作和共享

    一、 基础概念

    • 1. 进程和线程

      进程是操作系统进行资源分配的最小单位,同意进程中的多条线程共享该进程中的系统资源,
      线程是cpu调度的最小单位,必须依赖于进程而存在

    • 2. CPU核心数与线程数的关系

      多核心是单芯片多处理器,
      多线程是让同一个处理器上的多个线程同步执行并共享处理器的执行资源。

    • 3. 时间片轮转机制

      如果时间片结束时进程还在运行,则cpu将剥夺并分配给另一个进程,如果进程在时间片结束前阻塞或者结束 则 cpu进行切换,进程切换(有时称为上下文切换)是需要一定时间的,设置太短会导致过多的进程切换,设 置太长会引起短的交互请求的响应变差,将时间片设置为100ms是一个折中的选项。
      二、java中的线程

    • 1. 启动

      (1)A 继承Thread,然后new A.start()
      (2)A implements Runnale,然后交给Thread运行new Thread(new A()).start()
      总结:Thread才是jav对线程的唯一抽象,Runnable只是对任务对业务逻辑的抽象,Thread可以接受任何一个 Runnable实例

    • 2.中止

    (1)线程自然终止
    run执行完成了,或者抛出了一个未处理的异常导致线程提前结束。
    (2)stop
    暂停(suspend)、恢复(resume)、停止(stop)。suspend调用后线程不会释放已经占有的资源,二十站 着资源进入睡眠状态,容易引发死锁。stop中介一个线城时不能保证线程正常释放资源。所以这些方法被标注 为过期方法,不建议使用。
    (3)中断
    线程通过isInterrupt来进行判断是否被中断,如果一个线程时阻塞状态(sleep、join、wait),则在线程在检 查中断标识时,如果发现中断标识为true,则会在阻塞方法调用处抛出InterruptedException,并且立即将线程 中断标识位设置为false;

    • 3.run()和start()

      1. Threadrun方法,在new Thread时只是new出来了一个实例并未与真正的线程挂钩,调用start方法后才真正 启动线程,让线程进入到就绪队列等待分配cpu,分到cpu后才调用实现的run方法。
    • 4.yield()

      使当前线程让出cpu占有权,但让出的时间时不可设定的。也不会释放所资源,
      三、线程基础、线程之间的协作和共享

    • 1.线程间的共享

    (1)synchronized内置锁
    可以修饰方法、同步块,确保多个线程在同一时刻只有一个线程在方法或者同步块中。
    (2)对象锁和类锁
    对象锁是用于对象实例方法,或者一个对象实例。类锁适用于类的静态方法上,或者一个类的class对象上。
    (3)volatile
    volatile保证了多个线程对这个变量进行操作时的可见性,但是不能保证在多个线程写的安全性,适用于一个线 程写多个线程读。

    • 2.ThreadLocal

    为每个线程提供变量的副本,是的每个线程在同一时间访问的并非同一个对象,这样就隔离了多个线程对数据 的共享
    (1)解析:

    //该方法返回当前线程所对应的线程局部变量
    
    public T get() {
    
           Thread t = Thread.currentThread();
    
           ThreadLocalMap map = getMap(t);
    
           if (map != null) {
    
               ThreadLocalMap.Entry e = map.getEntry(this);
    
               if (e != null) {
    
                   @SuppressWarnings("unchecked")
    
                   T result = (T)e.value;
    
                   return result;
    
               }
    
           }
    
           return setInitialValue();
    
       }
    
    //设置当前线程的线程局部变量的值
    
    public void set(T value) {
    
           Thread t = Thread.currentThread();
    
           ThreadLocalMap map = getMap(t);
    
           if (map != null)
    
               map.set(this, value);
    
           else
    
               createMap(t, value);
    
       }
    
       /*
    
       *将当前线程局部变量的值删除,目的是为了减少内存的占用
    
       */
    
     public void remove() {
    
            ThreadLocalMap m = getMap(Thread.currentThread());
    
            if (m != null)
    
                m.remove(this);
    
      }
    
      /*
    
       *返回该线程局部变量的初始值这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
    
       */
    
        protected T initialValue() {
    
           return null;
    
       }
    

    ThreadLocalMap map = getMap(t);返回的是ThreadLocal的一个静态内部类,同时优势Thread的成员变量,下面重点看下ThreadLocalMap的代码片段

    
    static class Entry extends WeakReference<ThreadLocal<?>> {
    
               /** The value associated with this ThreadLocal. */
    
       Object value;
    
       //类似于map的key,value结构 key是ThreadLocal,value是需要隔离访问的白能量
    
        Entry(ThreadLocal<?> k, Object v) {
    
            super(k);
    
            value = v;
    
        }
    
    }
    
    //用数组保存了Entry,因为可能有多个变量需要线程隔离访问
    
    private Entry[] table;
    
    //
    
    static class Entry extends WeakReference<ThreadLocal<?>> {
    
        /** The value associated with this ThreadLocal. */
    
        Object value;
    
        Entry(ThreadLocal<?> k, Object v) {
    
            super(k);
    
            value = v;
    
        }
    
    }
    

    回顾我们的get方法,其实就是拿到每个线程独有的ThreadLocalMap然后再用ThreadLocal的当前实例,拿到Map中的相应的Entry,然后就可以拿到相应的值返回出去。当然,如果Map为空,还会先进行map的创建,初始化等工作。

    • 2.线程间的协作

    等待和通知的标准范式
    等待方遵循如下原则。
    1)获取对象的锁。
    2)如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件。
    3)条件满足则执行对应的逻辑。

    synchronized(对象){
    
    while(条件不满足){
    
     对象.wait();
    
    }
    
    }
    

    通知方遵循如下原则。
    1)获得对象的锁。
    2)改变条件。
    3)通知所有等待在对象上的线程。

    synchronized(对象){
    
    改变条件
    
    对象.notifyAll();
    
    }