Android面试题总结(四)线程,多线程,线程池

1.开启线程的三中方式?html

https://blog.csdn.net/longshengguoji/article/details/41126119(转)
java

https://blog.csdn.net/u012973218/article/details/51280044(转)
git

2.线程和进程的区别?github

https://blog.csdn.net/mxsgoden/article/details/8821936(转)
数据库

3.run()和start()方法区别?缓存

1.start()方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码:

经过调用Thread类的start()方法来启动一个线程,
这时此线程是处于就绪状态,
并无运行。
而后经过此Thread类调用方法run()来完成其运行操做的,
这里方法run()称为线程体,
它包含了要执行的这个线程的内容,
Run方法运行结束,
此线程终止,
而CPU再运行其它线程,

 

2.run()方法看成普通方法的方式调用,程序仍是要顺序执行,仍是要等待run方法体执行完毕后才可继续执行下面的代码:

而若是直接用Run方法,
这只是调用一个方法而已,
程序中依然只有主线程--这一个线程,
其程序执行路径仍是只有一条,
这样就没有达到写线程的目的。

 

举例说明一下:

记住:线程就是为了更好地利用CPU,
提升程序运行速率的!

public class TestThread1{
public static void main(String[] args){
Runner1 r=new Runner1();
//r.run();//这是方法调用,而不是开启一个线程
Thread t=new Thread(r);//调用了Thread(Runnable target)方法。且父类对象变量指向子类对象。
t.start();

for(int i=0;i<100;i++){
System.out.println("进入Main Thread运行状态");
System.out.println(i);
}
}
}
class Runner1 implements Runnable{ //实现了这个接口,jdk就知道这个类是一个线程
public void run(){

for(int i=0;i<100;i++){
System.out.println("进入Runner1运行状态");
System.out.println(i);
}
}
安全

}服务器

4.java线程中wait和sleep的不一样?多线程

https://www.cnblogs.com/DreamSea/archive/2012/01/16/2263844.html(转)
并发

5.谈谈对wait和notify关键字的理解?

  1. 使用wait()、notify()和notifyAll()时须要首先对调用对象加锁
  2. 调用wait()方法后,线程状态会从RUNNING变为WAITING,并将当线程加入到lock对象的等待队列中
  3. 调用notify()或者notifyAll()方法后,等待在lock对象的等待队列的线程不会立刻从wait()方法返回,必需要等到调用notify()或者notifyAll()方法的线程将lock锁释放,等待线程才有机会从等待队列返回。这里只是有机会,由于锁释放后,等待线程会出现竞争,只有竞争到该锁的线程才会从wait()方法返回,其余的线程只能继续等待
  4. notify()方法将等待队列中的一个线程移到lock对象的同步队列,notifyAll()方法则是将等待队列中全部线程移到lock对象的同步队列,被移动的线程的状态由WAITING变为BLOCKED
  5. wait()方法上等待锁,能够经过wait(long timeout)设置等待的超时时间

https://blog.csdn.net/u011116672/article/details/51044958(转自)

6.什么致使线程阻塞?

 线程在运行的过程当中由于某些缘由而发生阻塞,阻塞状态的线程的特色是:该线程放弃CPU的使用,暂停运行,只有等到致使阻塞的缘由消除以后才回复运行。或者是被其余的线程中断,该线程也会退出阻塞状态,同时抛出InterruptedException。

        致使阻塞的缘由有不少种,大体分为三种来讨论,分别是通常线程中的阻塞,Socket客户端的阻塞,Socket服务器端的阻塞。


通常线程中的阻塞:

        A、线程执行了Thread.sleep(int millsecond);方法,当前线程放弃CPU,睡眠一段时间,而后再恢复执行

        B、线程执行一段同步代码,可是尚且没法得到相关的同步锁,只能进入阻塞状态,等到获取了同步锁,才能回复执行。

        C、线程执行了一个对象的wait()方法,直接进入阻塞状态,等待其余线程执行notify()或者notifyAll()方法。

        D、线程执行某些IO操做,由于等待相关的资源而进入了阻塞状态。好比说监听system.in,可是尚且没有收到键盘的输入,则进入阻塞状态。


Socket客户端的阻塞:

        A、请求与服务器链接时,调用connect方法,进入阻塞状态,直至链接成功。

        B、当从Socket输入流读取数据时,在读取足够的数据以前会进入阻塞状态。好比说经过BufferedReader类使用readLine()方法时,在没有读出一行数据以前,数据量就不算是足够,会处在阻塞状态下。

        C、调用Socket的setSoLinger()方法关闭了Socket延迟,当执行Socket的close方法时,会进入阻塞状态,知道底层Socket发送完全部的剩余数据


Socket服务器的阻塞:

        A、线程执行ServerSocket的accept()方法,等待客户的链接,知道接收到客户的链接,才从accept方法中返回一个Socket对象

        B、从Socket输入流读取数据时,若是输入流没有足够的数据,就会进入阻塞状态

        D、线程向Socket的输出流写入一批数据,可能进入阻塞状态


        当程序阻塞时,会下降程序的效率,因而人们就但愿能引入非阻塞的操做方法。    

        所谓非阻塞方法,就是指当线程执行这些方法时,若是操做尚未就绪,就当即返回,不会阻塞着等待操做就绪。Java.nio 提供了这些支持非阻塞通讯的类。

7. 线程如何关闭?

https://blog.csdn.net/feiduclear_up/article/details/43270375(转)

https://blog.csdn.net/wuyupengwoaini/article/details/49537131(转)

8.java线程中同步的方法?

https://www.cnblogs.com/XHJT/p/3897440.html(转)

9.如何保证线程安全?

https://blog.csdn.net/xiangxianghehe/article/details/51135299(转)

10.谈谈对Synchronized关键字,类锁,方法锁,重入锁的理解?

https://blog.csdn.net/yuxin6866/article/details/79884723(转)

11.volatile的原理及使用

http://www.importnew.com/23520.html(转)

http://www.cnblogs.com/paddix/p/5428507.html(转)

https://monkeysayhi.github.io/2016/11/29/volatile%E5%85%B3%E9%94%AE%E5%AD%97%E7%9A%84%E4%BD%9C%E7%94%A8%E3%80%81%E5%8E%9F%E7%90%86/(转)

12.谈谈对NIO的理解

BIO,同步阻塞式IO,一个链接一个线程,这个线程只针对这个链接而存在,专一于它的收发,若是没有数据读入它就一直阻塞等待。固然能够经过线程池改善。

AIO,同步非阻塞式IO,一个请求一个线程,即客户端发送的链接请求都会注册到多路复用器上,多路复用器轮询到链接有I/O请求时才启动一个线程进行处理。用户进程也须要时不时的询问IO操做是否就绪,这就要求用户进程不停的去询问。 

NIO,异步非阻塞式IO,一个有效请求一个线程,用户进程只须要发起一个IO操做而后当即返回,等IO操做真正的完成之后,应用程序会获得IO操做完成的通知,此时用户进程只须要对数据进行处理就行了,不须要进行实际的IO读写操做。

13.synchronized 和volatile 关键字的区别

首先须要理解线程安全的两个方面:执行控制内存可见

执行控制的目的是控制代码执行(顺序)及是否能够并发执行。

内存可见控制的是线程执行结果在内存中对其它线程的可见性。根据Java内存模型的实现,线程在具体执行时,会先拷贝主存数据到线程本地(CPU缓存),操做完成后再把结果从线程本地刷到主存。

synchronized关键字解决的是执行控制的问题,它会阻止其它线程获取当前对象的监控锁,这样就使得当前对象中被synchronized关键字保护的代码块没法被其它线程访问,也就没法并发执行。更重要的是,synchronized还会建立一个内存屏障,内存屏障指令保证了全部CPU操做结果都会直接刷到主存中,从而保证了操做的内存可见性,同时也使得先得到这个锁的线程的全部操做,都happens-before于随后得到这个锁的线程的操做。

volatile关键字解决的是内存可见性的问题,会使得全部对volatile变量的读写都会直接刷到主存,即保证了变量的可见性。这样就能知足一些对变量可见性有要求而对读取顺序没有要求的需求。

使用volatile关键字仅能实现对原始变量(如boolen、 short 、int 、long等)操做的原子性,但须要特别注意, volatile不能保证复合操做的原子性,即便只是i++,实际上也是由多个原子操做组成:read i; inc; write i,假如多个线程同时执行i++volatile只能保证他们操做的i是同一块内存,但依然可能出现写入脏数据的状况。

在Java 5提供了原子数据类型atomic wrapper classes,对它们的increase之类的操做都是原子操做,不须要使用sychronized关键字。

对于volatile关键字,当且仅当知足如下全部条件时可以使用:

1. 对变量的写入操做不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。
2. 该变量没有包含在具备其余变量的不变式中。
  • 1
  • 2
  • 3

volatile和synchronized的区别

  1. volatile本质是在告诉jvm当前变量在寄存器(工做内存)中的值是不肯定的,须要从主存中读取; synchronized则是锁定当前变量,只有当前线程能够访问该变量,其余线程被阻塞住。
  2. volatile仅能使用在变量级别;synchronized则可使用在变量、方法、和类级别的
  3. volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则能够保证变量的修改可见性和原子性
  4. volatile不会形成线程的阻塞;synchronized可能会形成线程的阻塞。
  5. volatile标记的变量不会被编译器优化;synchronized标记的变量能够被编译器优化

14.死锁的必要条件和避免方式

https://blog.csdn.net/abigale1011/article/details/6450845(转)

15.对象锁和类锁是否会互相影响?

 对象锁:Java的全部对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,固然若是已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然能够由JVM来自动释放。

·        类锁:对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。其实类锁只是一个概念上的东西,并非真实存在的,它只是用来帮助咱们理解锁定实例方法和静态方法的区别的。咱们都知道,java类可能会有不少个对象,可是只有1Class对象,也就是说类的不一样实例之间共享该类的Class对象。Class对象其实也仅仅是1java对象,只不过有点特殊而已。因为每一个java对象都有1个互斥锁,而类的静态方法是须要Class对象。因此所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是MyClass.class的方式。

·        类锁和对象锁不是同1个东西,一个是类的Class对象的锁,一个是类的实例的锁。也就是说:1个线程访问静态synchronized的时候,容许另外一个线程访问对象的实例synchronized方法。反过来也是成立的,由于他们须要的锁是不一样的。


类锁,对象锁,私有锁

1. 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class)的代码段

2.对象锁:在代码中的方法上加了synchronized的锁,或者synchronized(this)的代码段

3.私有锁:在类内部声明一个私有属性如private Object lock,在须要加锁的代码段synchronized(lock)

 

对象锁:假设我有一个类ClassA,其中有一个方法synchronized methodA(),那么当这个方法被调用的时候你得到就是对象锁。

举例:ClassA a = new ClassA(); ClassA b = new ClassA(); 那么若是你在a这对象上调用了methodA,不会影响b这个对象,也就是说对于b这个对象,他也能够调用methodA,由于这是两对象,因此说对象锁是针对对象的

 

类锁,其实没有所谓的类锁,由于类锁实际上就是这个类的对象的对象锁

举例:我有一个类ClassA,其中有一个方法synchronized static methodA(),注意这个方法是静态的了,那就是说这个类的全部的对象都公用一个这个方法了,那若是你在这个类的某个对象上调用了这个方法,那么其余的对象若是想要用这个方法就得等着锁被释放,因此感受就好像这个类被锁住了同样。

16,线程,多线程,线程池的总结

https://www.jianshu.com/p/b8197dd2934c(转)

17.多线程断点续传原理

在下载大文件的时候,咱们每每要使用多线程断点续传,保证数据的完整性,首先说多线程,咱们要多线程下载一个大文件,就有开启多个线程,多个connection,既然是一个文件分开几个线程来下载,那确定就是一个线程下载一个部分,若是文件的大小是200M, 使用两个线程下载, 第一个线程下载1-100M, 第二个线程下载101-200M。 
咱们在请求的header里面设置

conn.setRequestProperty("Range", "bytes="+startPos+"-"+endPos);
  • 1

这里startPos是指从数据端的哪里开始,endPos是指数据端的结束 
根据这样咱们就知道,只要多个线程,按顺序指定好开始跟结束,就能够解决下载冲突的问题了。

如何写文件

byte[] buffer = new byte[1024];  
int offset = 0;   
RandomAccessFile threadFile = new RandomAccessFile(this.saveFile,"rwd");  
threadFile.seek(startPos);  
threadFile.write(buffer,0,offset);
  • 1
  • 2
  • 3
  • 4
  • 5

从上面代码能够看出,每一个线程找到本身开始写的位置,就是seek(startPos) 
这样就能够保证数据的完整性,也不会重复写入了

基本上多线程的原理就是这样,其实也很简单 那么咱们接着说断点续传,断点续传其实也很简单,原理就是使用数据库保存上次每一个线程下载的位置和长度 例如我开了两个线程T1,T2来下载一个文件,设文件总大小为1024M,那么就是每一个线程下载512M 但是个人下载中断了,那么我下次启动线程的时候(继续下载),是否是应该要知道,我原来下载了多少呢 因此是这样的,每下载一点,就更新数据库的数据,