1. 线程与进程的区别
(1)进程是具有一定独立功能的程序,是操作系统进行资源分配和调度的一个独立单位;
线程是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能独立运行的基本单位。
(2)进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫做轻量级进程。
(3)进程在执行时通常拥有独立的内存单元,而线程之间可以共享内存。
2.实现多线程程序的两种方法
实现多线程不需要在源程序前使用import语句显示导入thread类
方式1:继承Thread类。
A:自定义类MyThread继承Thread类。
B:MyThread类里面重写run():run Alt+/
C:创建对象
D:启动线程
方式2:实现Runnable接口的步骤:
A:自定义类MyRunnable实现Runnable接口
B:重写run()方法
C:创建MyRunnable类的对象
D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
3.Thread类要重写run()方法,为什么呢?
不是类中的所有代码都需要被线程执行的。
而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
4. run()和start()的区别?
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,调用该线程的run()方法。
5.Volatile的作用,实现原理,什么情况用
(1)用来修饰被不同线程访问和修改的变量,Volatile是轻量级的synchronized
(2)Volatile关键字为实例域的同步访问提供了一种免锁机制。生成的汇编代码多了一个lock前缀指令
(3)可以解决可见性、有序性(避免指令重排)。
6.线程的生命周期图
新建 -- 就绪(已经启动,但是没有执行权) -- 运行 -- 死亡
新建 -- 就绪 -- 运行 -- 阻塞(没有执行权,回到就绪) -- 就绪 -- 运行 -- 死亡
7.锁对象问题
(1)普通同步方法,锁是当前实例对象
(2)静态同步方法,锁是当前类的class对象
(3)同步方法块,锁是括号里面的对象:任意对象
8.同步方法和同步代码块的区别是什么?
在Java语言中,每一个对象有一把锁。线程可以使用synchronized关键字来获取对象上的锁。synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁)。
java 中每个对象都有一把锁, 线程可以通过 synchronized 关键字来获取对象上的锁
同步方法(粗粒度锁):
(1)修饰一般方法: public synchronized void method(){...}, 获取的是当前调用对象 this 上的锁
(2) 修饰静态方法: public static synchronized voidmethod (){...}, 获取当前类的字节码对象上的锁
同步代码块(细粒度锁):
synchronized ( obj ) {...}, 同步代码块可以指定获取哪个对象上的锁, obj 任意
9.当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?
不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。
10.线程安全的类
StringBuffer sb = new StringBuffer();
Vector<String> v = newVector<String>();// Vector基本不用
Hashtable<String, String> h = newHashtable<String, String>();
11.线程安全的ArrayList
List<String> list1 = newArrayList<String>();// 线程不安全
List<String> list2 =Collections.synchronizedList(new ArrayList<String>()); // 线程安全
12. Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?
sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。
wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(waitpool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
13.线程的sleep()方法和yield()方法有什么区别?
(1)sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
(2)线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
(3)sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
(4)sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
14.请说出与线程同步以及线程调度相关的方法
- wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
- notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
- notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
15. 什么是不可变对象,它对写并发应用有什么帮助?
不可变对象(英语:Immutable object)是一种对象,在被创造之后,它的状态就不可以被改变。任何试图改变改对象状态的操作都会生成新的对象,老的对象保持不变。
在多线程并发的情况下 ,多个线程读取共享变量是安全的,但是写操作肯定会冲突, 由于不可变对象不可更改,并发时不需要其他额外的同步保证,故相比其他的锁同步等方式的并发性能要好。
16.现在有t1、t2、t3三个线程,你怎样保证t2在t1执行完后执行,t3在t2执行完后执行?
在t3线程中, 调用t2.join , 在t2中,调用t1.join
17.进程之间的通信方式。
# 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
# 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
# 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
# 消息队列( message queue ) :消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
# 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
# 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
# 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机间的进程通信。
18.死锁的必要条件,怎么处理死锁。
当计算机系统同时具备下面4个条件时,会发生死锁:
(1)互斥条件。
某个资源在一段时间内只能由一个进程占有
(2)占有且等待条件。
进程至少已经占有一个资源,但又申请新的资源。
(3)不可抢占条件。
一个进程所占有的资源在用完之前,其他进程不能强行夺走该资源
(4)循环等待条件。
预防死锁:破坏四个必要条件之一
避免死锁:安全序列:进程按照某种次序分配单元
银行家算法