1.对《Java高并发编程详解:多线程与架构设计》书做一个总结(第一篇)

1.问题分析工具

  • jconsole

  • jstack

  • jstat

  • jvisualvm

  • jProfiler

前4个工具是在jdk的bin目录下的,最后一个是一个收费的工具。

还有一个class字节码反编译命令:javap -c class文件名


2.线程生命周期的状态

  • new:线程的创建。
  • Runnable:可运行状态,就是线程调用了start方法。
  • Blocked:阻塞状态,线程调用start方法后,但是还没获取到锁对象,所以进入了阻塞状态。
  • Wait:等待状态,当线程调用了start方法后并获取到了所对象,此时只需要等待CPU的调度(得到CPU的执行时间片),在这里个人的理解是:线程执行了wait方法后该线程的状态我也归为wait状态。万事俱备只欠东风。
  • 计时等待:当线程里面执行了sleep(millis)方法后,该线程没有释放锁对象,并等待指定时间后重新获取CPU的执行时间片使得到CPU的调度。

3.线程的start方法剖析:模板设计模式在Thread中的应用

其实Thread的run和start就是比较典型的模板设计模式,父类编写算法结构代码,子类实现逻辑细节。(具体可以看该书P9)

4.Runnable接口的引入以及策略模式在Thread中的使用

在很多软文以及一些书籍中,经常提到,创建线程的方式有两种,第一种是构造Thread类的对象或Thread子类的对象,第二种是实现Runnable接口,这种说法我觉得是错误的不严谨。因为在JDK中代表线程的只有Thread这个类。

解释一下我为什么认为实现Runnable接口不是创建线程的另一种方式

public class MyThread implements Runnable {
    @Override
    public void run() {
        //线程要执行的逻辑
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread t = new Thread(myThread);
        t.start();
    }
}

Thread类的run方法:

    @Override
    public void run() {
        //target是创建Thread对象时,通过构造方法的参数传进来的Runnable接口实现类的对象
        //在这里就是上面例子中的MyThread的对象
        //当target不为空时,线程其实执行的就是Runnable实现类的对象的run方法
        if (target != null) {
            target.run();
        }
    }

所以,通过实现Runnable接口来创建线程的方式,线程依然是Thread类,只是换了一种方式让线程来执行我们写的逻辑代码,这种方式的好处是:MyThread这样的类本来就需要继承一个类,但是又要实现线程要处理的逻辑,所以就可以采用实现Runnable接口的方法。但线程依然是Thread类的对象,而Runnable的run只是可以作为线程的执行单元。

4.4策略模式在Thread中的使用

再来看一下Thread类的run方法:
 

    @Override
    public void run() {
        if (target != null) {
            //创建每一个Thread对象并通过构造参数传入Runnable接口的实现了
            //最终调用Runnable接口实现类的run方法
            //其实传入的不同的Runnable接口实现类的对象,就相当于不同的策略
            target.run();
        }
    }

5.JVM内存结构

JVM内存结构图

1.程序计数器:程序计数器在JVM中所起的作用就是用于存放当前线程接下来将要执行的字节码指令、分支、循环、跳转、异常处理等信息。在任何时候,一个处理器只执行其中一个线程的命令,为了能够在CPU时间片轮转切换上下文之后顺利回到正确的执行位置,每条线程都需要具有一个独立的程序计数器,各个线程之间互不影响,因此JVM将此块内存区域设计成了线程私有的。

2.Java虚拟机栈:Java虚拟机站也是线程私有的,它的生命周期与线程相同,是在JVM运行时所创建的。在线程中,方法在执行的时候都会创建一个名为栈帧(stack frame)的数据结构,主要用于存放局部变量表、操作栈、动态链接、方法出口等信息。方法的调用也对应着栈帧在虚拟机中的压栈和弹栈过程。

Java虚拟机栈

3.本地方法栈:为调用Java本地方法服务的。Java中提供了调用本地方法的接口(Java Native Interface),也就是C/C++程序,在线程的执行过程中,经常会碰到调用JNI方法的情况,JVM为本地方法所划分的内存区域便是本地方法栈。这块内存区域其自由度非常高,完全靠不同的JVM厂商来实现,Java虚拟机规范并未给出强制性的规定,同样它也是县城私有的内存区域。

4.对内存:堆内存是JVM中最大的一块内存区域,被所有的线程共享,Java在运行期间创建的所有对象几乎都存放在该内存区域,该内存区域也是垃圾回收器重点照顾的区域,因此有些时候被称为“GC堆”。

对内存一般被细分为新生代和老年代,更细致划分:新生代中包含Eden区、S1和S2区。

在这里简单讲一下堆的垃圾回收和空间比例(HotSpotJVM为例)

新生代使用的垃圾回收算法是:复制算法

老年代使用的垃圾回收算法是:标记-清除-整理算法

Eden、S1和S2分别占用新生代空间的默认比例:8:1:1

java虚拟机堆空间结构图

5.方法区:方法区也是被线程共享的内存区域,他主要用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器(JIT)编译后的代码等数据。

6.使用TimeUnit替代Thread.sleep

7.正常关闭线程

7.1通过interrupt中断线程

7.2定义一个volatile修饰的变量,通过判断该变量的值来终止线程。

8.线程死锁

8.1交叉锁可导致程序出现死锁

public class DeadLock
{

    private final Object MUTEX_READ = new Object();
    private final Object MUTEX_WRITE = new Object();

    public void read()
    {
        synchronized (MUTEX_READ)
        {
            System.out.println(currentThread().getName() + " get READ lock");
            synchronized (MUTEX_WRITE)
            {
                System.out.println(currentThread().getName() + " get WRITE lock");
            }
            System.out.println(currentThread().getName() + " release WRITE lock");
        }
        System.out.println(currentThread().getName() + " release READ lock");
    }


    public void write()
    {
        synchronized (MUTEX_WRITE)
        {
            System.out.println(currentThread().getName() + " get WRITE lock");
            synchronized (MUTEX_READ)
            {
                System.out.println(currentThread().getName() + " get READ lock");
            }
            System.out.println(currentThread().getName() + " release READ lock");
        }
        System.out.println(currentThread().getName() + " release WRITE lock");
    }

    public static void main(String[] args)
    {
        final DeadLock deadLock = new DeadLock();
        new Thread(() ->
        {
            while (true)
            {
                deadLock.read();
            }
        }, "READ-THREAD").start();

        new Thread(() ->
        {
            while (true)
            {
                deadLock.write();
            }
        }, "WRITE-THREAD").start();
    }
}

9.锁对象关联的monitor对象

    public void a() {
        //每个对象的对象头会关联锁的信息,这个信息可以理解为就是对应的monitor对象。
        //也就是每个对象都有一个与之关联的monitor对象,这个对象中有两个队列
        //一个EntryList和WaitSet两个队列。
        //EntryList保存 向这个锁对象加锁没加成功的线程(因为前面的线程对锁对象还没释放)。
        //WaitSet保存 调用了这个所对象的wait方法进入了等待状态的线程。
        synchronized (obj) {
            //todo
            //obj.wait
        }
    }

 

monitor对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值