读《java多线程编程核心技术》后感

Java
323
0
0
2022-12-05
标签   Java多线程

第一章 java多线程技能

  • 进程是受操作系统管理的基本运行单元,它受系统进行资源分配和调度的一个独立单元。
  • 线程是进程中独立运行的子任务。
  • main函数也是一个进程,并且有一个main线程。
  • isAlive()方法判断当前的线程是否处于活动状态,也就是线程已经启动且尚未终止状态。
  • 停止线程:interrupt()方法停止线程,并不会真正停止线程,而是加一个停止标记。 interrupted()方法测试当前线程是否已经是中断状态,执行后具有将状态标志置清除为false的功能。isInterrupted()方法测试线程对象是否已经是中断状态,但不清除状态标志。stop()方法已经废弃,废弃原因1.可能使一些清理性的工作得不到完成;2.对锁定的对象会进行解锁导致数据得不到同步处理,出现数据不一致的问题。
  • 暂停线程suspend()和resume()方法已经废弃,废弃原因:1.容易造成锁的独占2.出现数据不同步
  • yield()方法的作用是放弃当前的cup资源,让给其他的任务区占用cpu执行时间,但放弃时间不确定,有可能立马又获取cpu时间片。
  • 线程的优先级分为1~10个等级。线程优先级高的线程得到的cpu资源较多,也就是cpu优先执行优先级较高的线程对象中的任务。线程优先级具有继承性,规则性,随机性。继承性:a线程启动b线程,b线程默认优先级和a一样。规则性:cpu尽量将执行资源让给优先级比较高的线程。随机性:并不是优先级越高就一定先执行,只是大概率获取cpu资源。
  • 守护线程。当进程中不存在非守护线程,守护线程自动销毁。
  • println方法在多线程中有可能阻塞
public void println(String x) {  
  synchronized (this) {  
      print(x);  
      newLine();  
  }  
}  

第二章 对象及变量的并发访问

  • 可重入锁:自己可以再次获取自己的内部锁。如果不可重入的话会造成死锁。
  • synchronized同步方法:1对其他synchronized同步方法和synchronized(this)同步块调用呈阻塞状态2.同一时间只有一个线程可以执行synchronized同步方法中的代码
  • synchronized(this)同步代码块:1对其他synchronized同步方法和synchronized(this)同步块调用呈阻塞状态2.同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码
  • 静态同步synchronized方法和synchronized(class)代码块对Class类进行持锁,而不是对象。
  • 由于在jvm中具有Stirng常量池缓存对功能,所以大部分情况,synchronized代码块尽量不要使用Stirng对象进行加锁,可以new Object()来使用。
  • volatile 解决了变量在多线程间对可见性。关键字volatile对作用是强制从公共堆栈中取得变量对值,而不是从线程私有数据栈中取得变量对值。

第三章 线程间通信

  • 等待/通知机制 在执行wait()方法之前,线程必须获得该对象对对象级锁,执行wait()方法后,当前线程立即释放锁。在执行notify()方法之前,线程必须获得该对象对对象级锁,执行notify()方法后,当前线程不会立即释放该对象锁,而是要将程序执行完。
  • 生产者/消费者模式,多生产者/消费者情况下为了防止假死状态使用notifyAll
package com.zlc.jzlc;

public class ValueObject {
    public static String value = "";
}
package com.zlc.jzlc;

public class P {
    private String lock;

    public P(String lock) {
        this.lock = lock;
    }

    public void setValue() {
        try {
            synchronized (lock) {
                if (!ValueObject.value.equals("")) {
                    lock.wait();
                }
                String value = System.currentTimeMillis() + "_" + System.nanoTime();
                System.out.println("P set:" + value);
                ValueObject.value = value;
                lock.notify();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package com.zlc.jzlc;

public class C {
    private String lock;

    public C(String lock) {
        this.lock = lock;
    }

    public void getValue() {
        try {
            synchronized (lock) {
                if (ValueObject.value.equals("")) {
                    lock.wait();
                }
                System.out.println("C get:" + ValueObject.value);
                ValueObject.value = "";
                lock.notify();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
package com.zlc.jzlc;

public class ThreadP extends Thread {
    private P p;

    public ThreadP(P p) {
        this.p = p;
    }

    public void run() {
        while (true) {
            p.setValue();
        }
    }
}
package com.zlc.jzlc;

public class ThreadC extends Thread {
    private C c;

    public ThreadC(C c) {
        this.c = c;
    }

    public void run() {
        while (true) {
            c.getValue();
        }
    }
}
package com.zlc.jzlc;

public class PCTest {
    public static void main(String[] args) {
        String lock = new String("");
        P p = new P(lock);
        C c = new C(lock);
        ThreadP tp = new ThreadP(p);
        ThreadC tc = new ThreadC(c);
        tp.start();
        tc.start();
    }
}
  • 通过管道进行线程间通信:字节流/字符流
  • ThreadLocal生成副本,每个副本都是独立的。
  • join()方法内部实现是wait/notify 使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z后面的代码。

第四章 Lock的使用

  • 公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的fifo先进先出顺序。而非公平锁不一定先来先得到。
  • 读写锁。读锁也即共享锁。写锁也即排他锁。读读不互斥,读写互斥,写写互斥。

其他

  • 完美单例 使用DCL双检查锁机制
package com.zlc.jzlc;

public class MyObject {
    private volatile static MyObject myObject;

    private MyObject() {
    }

    public static MyObject getInstance() {
        try {
            if (myObject == null) {
                //do some things
                synchronized (MyObject.class) {
                    if (myObject == null) {
                        myObject = new MyObject();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return myObject;
    }
}
  • 线程间的几种不同状态
  1. 有序列表项1新建状态(New):新创建了一个线程对象。
  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
  3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种: (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。 (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。 (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。