整理多个大神的资源,加上自己的一些整理:
https://www.cnblogs.com/king-garden/p/5672853.html
http://www.cnblogs.com/pureEve/p/6546288.html
https://blog.csdn.net/java_2017_csdn/article/details/78257635
https://www.cnblogs.com/aspirant/p/6920418.html
https://www.cnblogs.com/felixzh/p/6036074.html
1,什么是线程、进程,区别是什么
1进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元
2同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进程至少包括一个线程。
3进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5,进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,
进程是系统进行资源分配和调度的一个独立单位.
2,实现线程的方式(https://www.cnblogs.com/felixzh/p/6036074.html)
1、继承Thread类创建线程
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:
public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } } MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start();
2、实现Runnable接口创建线程
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口,如下:
为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
3、线程池(https://www.cnblogs.com/aspirant/p/6920418.html)
1、什么是线程池: java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
一个线程池包括以下四个基本组成部分:
1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
//创建一个可重用固定线程数的线程池 ExecutorService pool=Executors.newSingleThreadExecutor(); //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口; Thread t1=new MyThread(); //将线程放到池中执行; pool.execute(t1); //关闭线程池 pool.shutdown();
3、如何在两个线程间共享数据?
你可以通过共享对象来实现这个目的,或者是使用像阻塞队列这样并发的数据结构。(涉及到在两个线程间共享对象)用 wait 和 notify 方法实现了生产者消费者模型。
4、 Java 中 notify 和 notifyAll 有什么区别?
notify ()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll ()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。
5、为什么 wait, notify 和 notifyAll 这些方法不在 thread 类里面?
一个很明显的原因是 JAVA 提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的 wait ()方法就有意义了。如果 wait ()方法定义在 Thread 类中,线程正在等待的是哪个锁就不明显了。简单的说,由于 wait,notify 和 notifyAll 都是锁级别的操作,所以把他们定义在 Object 类中因为锁属于对象。
6、Java 中如何停止一个线程?
Java 提供了很丰富的 API 但没有为停止线程提供 API。JDK 1.0 本来有一些像 stop (), suspend () 和 resume ()的控制方法但是由于潜在的死锁威胁因此在后续的 JDK 版本中他们被弃用了,之后 Java API 的设计者就没有提供一个兼容且线程安全的方法来停止一个线程。当 run () 或者 call () 方法执行完的时候线程会自动结束,如果要手动结束一个线程,你可以用volatile 布尔变量来退出 run ()方法的循环或者是取消任务来中断线程7、Java 中堆和栈有什么不同?
每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值。
8、生产中消费者模型(https://blog.csdn.net/bit_clearoff/article/details/55805884)9、如何避免死锁?
Java 多线程中的死锁
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。
10、JVM参数详细列表(https://blog.csdn.net/he523160876/article/details/52331678)
11、synchronized和lock的区别(https://blog.csdn.net/u012403290/article/details/64910926)锁类型
-
可重入锁:在执行对象中所有同步方法不用再次获得锁
-
可中断锁:在等待获取锁过程中可中断
-
公平锁: 按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁权利
-
读写锁:对资源读取和写入的时候拆分为2部分处理,读的时候可以多线程一起读,写的时候必须同步地写
类别 | synchronized | Lock |
---|---|---|
存在层次 | Java的关键字,在jvm层面上 | 是一个类 |
锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 | 在finally中必须释放锁,不然容易造成线程死锁 |
锁的获取 | 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 | 分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待 |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入 不可中断 非公平 | 可重入 可判断 可公平(两者皆可) |
性能 | 少量同步 | 大量同步 |
synchronized用法:https://blog.csdn.net/luoweifu/article/details/46613015、
12、Java 多线程中调用 wait () 和 sleep ()方法有什么不同?
Java 程序中 wait 和 sleep 都会造成某种形式的暂停,它们可以满足不同的需要。wait ()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而 sleep ()方法仅仅释放 CPU 资源或者让当前线程停止执行一段时间,但不会释放锁。
13、写出 3 条你遵循的多线程最佳实践
- 给你的线程起个有意义的名字。
这样可以方便找 bug 或追踪。OrderProcessor, QuoteProcessor or TradeProcessor 这种名字比 Thread-1. Thread-2 and Thread-3 好多了,给线程起一个和它要完成的任务相关的名字,所有的主要框架甚至 JDK 都遵循这个最佳实践。 - 避免锁定和缩小同步的范围
锁花费的代价高昂且上下文切换更耗费时间空间,试试最低限度的使用同步和锁,缩小临界区。因此相对于同步方法我更喜欢同步块,它给我拥有对锁的绝对控制权。 - 多用同步类少用 wait 和 notify
首先,CountDownLatch, Semaphore, CyclicBarrier 和 Exchanger 这些同步类简化了编码操作,而用 wait 和 notify 很难实现对复杂控制流的控制。其次,这些类是由最好的企业编写和维护在后续的 JDK 中它们还会不断优化和完善,使用这些更高等级的同步工具你的程序可以不费吹灰之力获得优化。 - 多用并发集合少用同步集合 这是另外一个容易遵循且受益巨大的最佳实践,并发集合比同步集合的可扩展性更好,所以在并发编程时使用并发集合效果更好。如果下一次你需要用到 map,你应该首先想到用 ConcurrentHashMap。
补充:
Java多线程中的竞争条件、锁以及同步的概念:https://blog.csdn.net/hourui93/article/details/48596259
其他问题题目(大神整理):https://blog.csdn.net/java_2017_csdn/article/details/78257635