1、并发编程的三个重要特性
-
原子性 : 一个的操作或者多次操作,要么所有的操作全部都得到执行并且不会收到任何因素的干扰而中断,要么所有的操作都执行,要么都不执行。
synchronized可以保证代码片段的原子性。 -
可见性 :当一个变量对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。
volatile关键字可以保证共享变量的可见性。 -
有序性 :代码在执行的过程中的先后顺序,Java 在编译器以及运行期间的优化,代码的执行顺序未必就是编写代码时候的顺序。
volatile关键字可以禁止指令进行重排序优化。
2、创建线程的几种方法
-
继承Thread类,重写run()方法,利用Thread.start()启动线程。
-
实现Runnable接口,重写run()方法,通过new Thread(Runnable a)创建线程,并调用start()方法启动线程。
-
通过callable和futuretask创建线程,实现callable接口,重写call方法,利用future对象包装callable实例,通过new Thread方法创建线程。
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大:call()方法可以有返回值,可以声明抛出异常。
- 通过线程池创建线程。
3、多线程的优缺点
优点:
- 充分利用多核多cpu的资源,提高cpu的使用率,提高了程序的运行效率。
缺点:
- 线程数过多会影响性能,操作系统会在线程切换之间增加内存的开销。
- 存在线程同步和安全问题
- 可能产生死锁
- 增加了开发人员的技术难度
线程有几种状态
一共五种状态:分别是新建,就绪,运行,阻塞和死亡状态。详细见下图:
- 新建状态: 当用new创建一个线程时,线程还没有开始运行,此时线程处于新建状态。处于新建状态的线程还没有开始运行。
- 就绪状态: 一个新建的线程并不会自动运行,要执行线程,要手动调用线程的start()方法,当start()方法返回后,线程就处于就绪状态,等待处理器的调度。
- 运行状态: 当线程获取了CPU的时间后,它才进入运行状态,真正的执行run()方法里的内容。
- 阻塞状态: 线程运行过程中,可能因为各种原因进入阻塞状态:比如调用sleep()进入休眠状态;调用一个在IO上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;等待获取锁被阻塞;线程在等待其他的触发条件。所谓的阻塞状态就是正在运行的线程没有运行结束,暂时让出CPU资源。
- 死亡状态: 有两个原因会导致线程死亡:run()方法正常结束;一个未捕获的异常终止了run()方法而导致线程猝死。