JVM和GC

内存分区

程序计数器和本地方法栈 略
栈:基本数据类型、对象的引用
堆:对象实例、数组
方法区:已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据等

多线程

线程生命周期

  1. 新建:被new出来
  2. 就绪:调用了start()后
  3. 运行:开始执行run()
  4. 阻塞:wait()、同步锁、sleep()
  5. 死亡:调用stop()、线程抛出异常或错误、run()执行完正常结束

    创建线程的方式

  6. 继承Thread类

  7. 实现Runnable接口
  8. 实现Callable接口

    线程安全问题

    线程安全问题指的是在某一线程从开始访问到结束访问某一数据期间,该数据被其他的线程所修改,那么对于当前线程而言,该线程就发生了线程安全问题,表现形式为数据的缺失,数据不一致等。
    解决思路:
        1)尽量不使用共享变量,将不必要的共享变量变成局部变量来使用。
        2)使用synchronized关键字同步代码块,或者使用jdk包中提供的Lock为操作进行加锁。
        3)使用ThreadLocal为每一个线程建立一个变量的副本,各个线程间独立操作,互不影响。
    ThreadLocal:是一个类似HashMap的数据结构,只能保存一个k-v键值对,线程之间互不干扰。用完记得调用remove()方法,防止内存泄漏。

    run()与start()

  9. start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码。

  10. 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。
  11. 方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运行 run 函数当中的代码。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。

    sleep 与 wait 区别

  12. 对于 sleep()方法,我们首先要知道该方法是属于 Thread 类中的。而 wait()方法,则是属于Object 类中的。

  13. sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
  14. 在调用 sleep()方法的过程中,线程不会释放对象锁。
  15. 而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。(wait、notify 方法必须在 synchronized 块或方法中被调用,并且要保证同步块或方法的锁对象与调用 wait、notify 方法的对象是同一个,如此一来在调用 wait 之前当前线程就已经成功获取某对象的锁,执行 wait 阻塞后当前线程就将之前获取的对象锁释放。)