1. 什么是线程?线程和进程的区别?

    1.进程是系统进行资源分配的基本单位,有独立的内存地址空间; 线程是CPU调度的基本单位,没有单独地址空间,有独立的栈,局部变量,寄存器, 程序计数器等。
    2.创建进程的开销大,包括创建虚拟地址空间等需要大量系统资源; 创建线程开销小,基本上只有一个内核对象和一个堆栈。
    3.一个进程无法直接访问另一个进程的资源;同一进程内的多个线程共享进程的资 源。
    4.进程切换开销大,线程切换开销小;进程间通信开销大,线程间通信开销小。
    5.线程属于进程,不能独立执行。每个进程至少要有一个线程,成为主线程。

  2. 描述CPU和多线程的关系

    • 第一阶段,单CPU时代,单CPU在同一时间点,只能执行单一线程。
    • 第二阶段,单CPU多任务阶段,计算机在同一时间点,并行执行多个线程。但这并非真正意义上的同时执行,而是多个任务共享一个CPU,操作系统协调CPU在某个时间点,执行某个线程,因为CPU在线程之间切换比较快,就好像多个任务在同时运行。
    • 第三阶段,多CPU多任务阶段,真正实现的,在同一时间点运行多个线程。具体到哪个线程在哪个CPU执行,这就跟操作系统和CPU本身的设计有关了。
  3. 什么是线程安全/线程不安全?

    线程安全:在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

    线程不安全:就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据

  4. wait、sleep、join、yield的区别

    sleep 方法是属于 Thread 类中的,sleep 过程中线程不会释放锁,只会阻塞线程,让出cpu给其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态,可中断,sleep 给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会

    wait 方法是属于 Object 类中的,wait 过程中线程会释放对象锁,只有当其他线程调用 notify 才能唤醒此线程。wait 使用时必须先获取对象锁,即必须在 synchronized 修饰的代码块中使用,那么相应的 notify 方法同样必须在 synchronized 修饰的代码块中使用,如果没有在synchronized 修饰的代码块中使用时运行时会抛出IllegalMonitorStateException的异常

    yield 和 sleep 一样都是 Thread 类的方法,都是暂停当前正在执行的线程对象,不会释放资源锁,和 sleep 不同的是 yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。还有一点和 sleep 不同的是 yield 方法只能使同优先级或更高优先级的线程有执行的机会

    join 等待调用join方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之后才能继续运行的场景。例如:主线程创建并启动了子线程,如果子线程中药进行大量耗时运算计算某个数据值,而主线程要取得这个数据值才能运行,这时就要用到 join 方法了

  5. Synchronized和Lock的区别

    1. 来源及用法:
      lock是一个接口,是java写的控制锁的代码,而synchronized是java的一个内置关键字,synchronized是托管给JVM执行的;
      synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
      lock:一般使用ReentrantLock类做为锁。在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。
    2. 异常是否释放锁:
      synchronized在发生异常时候自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)
    3. 是否响应中断
      lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;
    4. 是否阻塞
      Lock可以尝试获取锁,synchronized获取不到锁只能一直阻塞
      synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
    5. 锁特点
      synchronized的锁可重入不可中断非公平,而Lock锁可重入等待时可中断可判断可公平可非公平
    6. 性能差别

    synchronized少量同步,lock大量同步

    Java1.5中,synchronized是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。但是到了Java1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在Java1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地。
    如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。而且Lock可以提高多个线程进行读写操作的效率。(可以通过readwritelock实现读写分离)

    1. 等待通知
      synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度
    2. 锁机制
      1.synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。
      2.Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
  6. ThreadLocal、Volatile、Synchronized 的作用和区别

    volatile变量每次被线程访问时,都强迫线程从主内存中重读该变量的最新值,而当该变量发生修改变化时,也会强迫线程将最新的值刷新回主内存中。这样一来,不同的线程都能及时的看到该变量的最新值。

    但是volatile不能保证变量更改的原子性

    ThreadLocal不是为了解决多线程访问共享变量,而是为每个线程创建一个单独的变量副本,提供了保持对象的方法和避免参数传递的复杂性。

    1. volatile不需要同步操作,所以效率更高,不会阻塞线程,但是适用情况比较窄
    2. volatile读变量相当于加锁(即进入synchronized代码块),而写变量相当于解锁(退出synchronized代码块)
    3. synchronized既能保证共享变量可见性,也可以保证锁内操作的原子性;volatile只能保证可见性
  7. 同步方法和同步块,哪个更好?

    同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对

    象)。同步方***锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通

    常会导致他们停止执行并需要等待获得这个对象上的锁。

    同步块更要符合开放调用的原则,只在需要锁住的代码块锁住相应的对象,这样

    从侧面来说也可以避免死锁。

  8. 什么是死锁?如何避免死锁?

    线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。

    当然死锁的产生是必须要满足一些特定条件的:
    1.互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放
    2.请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
    3.不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用
    4.循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

    加锁顺序:确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生
    加锁时限:另外一个可以避免死锁的方法是在尝试获取锁的时候加一个超时时间,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。
    死锁检测:死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。

  9. 常见的线程池叫什么,线程池的作用是什么

    1.newCachedThreadPool创建一个可缓存线程池**

    2.newFixedThreadPool 创建一个定长线程池

    3.newScheduledThreadPool 创建一个定长线程池

    4.newSingleThreadExecutor 创建一个单线程化的线程池

    使用线程池的好处有很多,比如节省系统资源的开销,节省创建和销毁线程的时间等.