1.线程与进程的关系:
进程是电脑中的一个事物。而线程是这个事物中的一个处理单元,进程中可能包含多个线程。
2.什么是多线程:
多线程就是CPU在处理一个事物时多个单元同时执行
3.多线程会引发什么问题,及他的优缺点是什么?
多线程有可能会导致数据的不同步,他的优点是能使多个任务同时进行,但是过多使用反而会降低计算机性能。
4.如何避免线程的安全问题:
在java中使用synchronized关键字使代码同步,还可以使用Lock对象为必要的代码部分加锁与解锁。相比前者后者比较灵活更具扩展性。
5.synchronized使用的方式有几种,分别是什么,需要注意什么?
synchronized关键字可以加在代码块上面,格式synchronized(obj){},在代码块中执行的代码将得到同步。其中obj是同步代码块的锁对象,如果锁不是同一个对象同样会导致不同步的现象出现。
另外一种是使用同步函数,格式是public synchronized void test(){}。该种使用方式所使用的锁是当前函数所属的对象,也就是this。
6.synchronized使用在static修饰的方法上时,有什么不同?
当synchronized关键字加载static修饰的 方法上时,所使用的锁是当前方法所在对象的.class对象。这是因为静态修饰的方法是随着类的加载而加载的。
7.创建线程的方式有哪些?分别有什么好处
有两种方法:
第一种:继承thread类,重写run方法,在run方法中写要执行的代码。在创建线程对象时传入子类对象。
第二种:实现Runable接口,重写run方法。在创建线程对象时候传入该接口的子类对象。
建议在开发中都使用实现Runable接口,因为在java中不支持多继承,这样也更能体现面相对象的思想。
8.多线程开发中如何获取当前线程的名称:
currentThread.getName();
9.多线程开发中的异常信息有什么特点:
在只有main方法的时候由于只有一个线程,所以未标注线程名称,在多线程中异常信息前会标注异常的线程名称。
10.简单描述一下线程的不同状态:
多个线程同时启动的时候,每个线程大概会有五种状态,也有分成六种状态,第一种状态是创建,当newThread类或者其子类的时候,线程被创建,第二种状态是运行状态,当线程对象调用start方法线程启动并得到CPU执行权的时候,线程被执行。第三种是消亡状态,当run方法被执行完毕线程结束,或者调用stop(已过时)方法线程终止。第四种是冻结状态,或者分成睡眠状态和等待状态,线程对象调用wait方法进入等待状态(notify方法可以唤醒)。sleep(time)方法进入睡眠,时间到线程唤醒,当线程被唤醒,和start方法执行时,线程并不一定立马被执行,由于单个cpu只能一次执行一个线程,没被执行的线程则处于临时阻塞状态,但是这个时间非常短暂,cpu会随机的执行每一个线程。
11.什么是线程的死锁?
一个线程中的同步块里面嵌套的同步块有另一个线程的同步块锁对象,对方线程同步块里面的嵌套同步块又同时拥有前一个线程里的外层同步块的锁。就会出现死锁现象。这种情况下,每个线程都拿着对方的锁不放,导致线程都卡在那不动,这种现象叫做死锁。
12.等待唤醒机制:
wait(); 使线程处于等待状态,其实 把线程临时存储到了线程池中;
notify();唤醒任意一个线程;
notifyAll();唤醒所有线程。
等待和唤醒必须使用在同步中。因为必须标示wait和notify所对应的锁,同一个锁对象下的只能唤醒该锁下的线程。
为什么这些方法定义在object中,因为锁可以是任意对象,而任意对象都能调用的方法必然在object中。
13.在等待唤醒机制里面,我们经常会做一些条件判断让线程等待,但是如果用if()语句会存在弊端,也就是当线程被再次唤醒时候会直接执行后边代码,这样会存在不安全风险,而是要使用while()语句来开发,在线程唤醒之后再次执行条件,再判断等待或者执行后边代码。这种情况应用在多个线程同时执行时场景例如多生产多消费。
14.
使用synchronized进行同步不太灵活,当多生产多消费的时候往往要使用notifyall()方法,该方法会唤醒所有线程,这样的话就会造成程序的效率降低。如果使用多个synchronized嵌套虽然可以解决问题但是会容易造成死锁的问题。
那么怎样解决以上问题呢?在JDK1.4以前是只能那样做。在JDK1.5以后添加了Lock接口,和condition接口,Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。随着灵活性的增加,也带来了一些问题。不使用块结构锁就失去了使用 synchronized 方法和语句时会出现的锁自动释放功能。我们必须把执行代码放在一个块里面,而锁的释放给放在finnally里。详细可查API crourrent包下的lock接口。
小例子:
Lock l = new Reentrant();
l.lock();
try{
//do something....
}finally{
l.unlock();
}
15.condition接口的使用:
先创建锁对象,由锁对象构建condition对象。condition对象里面包装了wait,notify,notifyAll,成为await,signal,signalAll。
16.注意在使用等待唤醒机制的时候注意唤醒的是同一把锁下的线程,所以要使用锁对象下的wait,notify,notifyAll。
17.多线程编程开发中最核心的安全问题就是,多段代码共享同一数据,那么就需要考虑同步。
18.sleep与wait的区别:
1.sleep方法必须制定线程休眠的时间
wait方法可以制定也可以不指定线程停止的时间。
2.sleep方法执行后不会释放监视器锁权限,此时处于临时阻塞状态。
wait方法则在执行后立即释放锁权限
3.sleep方法到时间后自动唤醒并继续执行
wait方法则在执行后如果没有被notify或者notifyAll唤醒则一直处于等待状态。
4.sleep方法不一定要定义在同步中
wait方法则必须在同步中使用
19.怎样让一个线程停止?
run方法的结束就意味着线程的结束。往往在多线程开发中,都会使用循环,我们只要控制循环的入口使其停止循环并执行结束run方法的所有代码即可终止线程。但是这里边存在些小问题,那就是当线程一旦处于wait状态后就不行了。这时候就算线程被再次唤醒,可是不会再次判断标记状态,所以还会继续执行。我们需要调用线程的interrupt方法使其强制退出中断状态即可。伴随着会跑出interruptExecption,catch后进行循环入口标记的变更线程执行完毕结束。
另一种停止的方法是调用线程的stop方法,该方法已经过时,因为有不安全因素。它会终止所有正在执行包括执行一半的线程。
20.守护线程的概念:
守护线程其实指的是后台线程,我们正常创建的线程都是前台线程,当调用线程的setDaemon方法后该线程会被转换成后台线程,也就是守护线程,守护线程的作用是配合前台线程一起运行完成某些事情,而前台线程都结束后虚拟机会认为该进程结束,所有的后台线程都会结束,哪怕是后台线程仍然处于等待状态。
21.线程优先级的概念:
线程在创建以后是具备默认优先级的,默认的优先级是当前线程的优先级,也就是被哪个线程创建就是默认是哪个线程的优先级(比如main主线程)。线程的优先级是使用1-10的数字表示,数字越大优先级越高。默认的优先级是5。
在Thread类中已经定义了优先级的字段。MAX_PRIORITY,NORM_PRIORITY,MIN_PRIORITY。使用setPriority方法可以对线程进行优先级的设定。
22.线程组的概念:
线程组就是一个集合,存放了多个单独的线程,线程组有封装的类ThreadGroup,该类里面定义了常用的一些操作方法。在创建线程的时候可以指定该线程的所属线程组。这样的好处是便于线程的管理,比如批量停止线程等。