用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。

1、线程实现的方式

  • 继承Thread类
  • 实现Runnable接口
  • 使用Callable和Future

2、Thread中的start()run()有什么区别

start()方法用来启动线程,真正实现了多线程,无需等待run方法执行完毕

run()当作普通方法的方式调用,程序顺序执行,要等run方法执行完毕后才能继续执行下面的方法

3、线程的状态

  • NEW状态

    new了一个Thread对象,并没有启动该线程

  • RUNNABLE状态

    线程对象通过start()方法进入runnable状态,启动的线程不一定会立即得到执行,线程的运行与否要看cpu的调度,我们把这个中间状态叫可执行状态(RUNNABLE)

  • RUNNING状态

    一旦cpu通过轮询货其他方式从任务可以执行队列中选中了线程,此时它才能真正的执行自己的逻辑代码

  • BLOCKED状态

    线程正在等待获取锁

    调用了wait()或者sleep()、进行了某个阻塞IO操作、获得了某个资源的锁

  • TERMINATED状态

    TERMINATED是一个线程的最终状态

    线程运行正常结束、线程运行出错意外结束、JVM Crash 导致所有的线程都结束

线程状态转换

4、Java解决线程安全的四种方式

乐观锁与悲观锁

悲观锁,顾名思义它是悲观的。讲得通俗点就是,认为自己在使用数据的时候,一定有别的线程来修改数据,因此在获取数据的时候先加锁,确保数据不会被线程修改。形象理解就是总觉得有刁民想害朕。

而乐观锁就比较乐观了,认为在使用数据时,不会有别的线程来修改数据,就不会加锁,只是在更新数据的时候去判断之前有没有别的线程来更新了数据。具体用法在下面讲解。

方法一:使用synchronized关键字

一个表现为原生语法层面的互斥锁,它是一种悲观锁,使用它的时候我们一般需要一个监听对象 并且监听对象必须是唯一的,通常就是当前类的字节码对象。它是JVM级别的,不会造成死锁的情况。使用synchronized可以拿来修饰类,静态方法,普通方法和代码块。比如:Hashtable类就是使用synchronized来修饰方法的

方法二:Lock接口下的实现类

方法三:使用线程本地存储ThreadLocal

方法四:使用乐观锁机制

5、高并发中的集合有哪些问题

  • 第一代线程安全集合类

Vector、Hashtable

使用synchronized修饰方法,效率低下

  • 第二代线程非安全集合类

ArrayList、HashMap

线程不安全但是性能好

  • 第三代线程安全集合类

在大量并发情况下如何提高集合的效率和安全呢

Java容器、Java的发展历史