Q:开启一个线程的方法有哪些?销毁一个线程的方法呢?
- 直接使用Thread类。
- 使用Runnable和Thread。
- 使用Runnable和线程池。
- 使用AsyncTask。
- 使用HandlerThread。
- 使用IntentService。
直接使用Thread类开启子线程
这是最简单开启子线程的方法,也是最本质的方法,其他开启子线程的方法都是在此方法基础上的扩展。
一,使用示例如下:
new Thread(){
@Override
public void run() {
super.run();
}
}.start();
使用Thread类开启子线程一共分为三步:
- 创建Thread的子类对象。
- 重写run方法。
- 调用start方法开启子线程。
此时就开启了子线程,run方法执行在子线程中。
使用Runnable接口和Thread类开启子线程
此种方法使用的是Thread类的有参构造创建线程对象。
先实例实现了 Runnable 接口的类,之后在实例化一个 Thread,把接口类作为参数传递进去。之后调用 start 方法即可开启。
使用Runnable和线程池开启子线程
线程池中维护的是线程,所以使用线程池开启子线程本质还是使用的Thread类,因为用到的类和方法不同,所以单独列了出来。
使用示例如下:
ExecutorService threadPool = Executors.newFixedThreadPool(5);
threadPool.submit(new Runnable() {
@Override
public void run() {
}
});
使用AsyncTask开启子线程
AsyncTask是一个类,里面封装了Thread和Handler,开启了子线程,实现了线程间的通讯。
使用示例
new AsyncTask() {
protected void onPreExecute() {
super.onPreExecute();
}
protected Boolean doInBackground(String... params) {
publishProgress(1);
return null;
}
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
}
}.execute("abc");
使用HandlerThread开启子线程
HandlerThread是一个类,他继承了Thread类,并重写了run方法,在run方法中使用Looper创建了消息队列,此时就可以在这种Thread中直接使用Handler了。
一,使用示例
HandlerThread mHandlerThread = new HandlerThread("MyHandlerThread");;
mHandlerThread.start();//开启子线程
Handler mhandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//这个方法执行在子线程,里面可以做耗时的操作
}
};
mhandler.sendEmptyMessage(10);
分析:由于mHandlerThread.getLooper()得到的是子线程中的looper对象,所以mhandler是子线程中的handler,所以handleMessage方法也执行在子线程。
使用intentService开启子线程
IntentService这是一个特殊的服务,它是一个抽象类,继承了Service。里面封装了HandlerThread和Handler,本质等效于在服务中开启一个子线程。而且这个服务可以在子线程执行结束后自动关闭。
一,使用示例
public static class MyIntentService extends IntentService{
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(Intent intent) {
//这个方法执行在子线程,里面可以做耗时的操作
}
}
分析:自定义一个类,继承intentService,然后重写onHandleIntent方法,这个方法执行在子线程。
Q:Thread的join()有什么作用?
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
t.join(); //使调用线程 t 在此之前执行完毕。
t.join(1000); //等待 t 线程,等待时间是1000毫秒
volatile和synchronized的区别
- volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
tips:volitate不是用来同步的,主要还是获取最新数据
同步方法和同步方法块锁住的是谁?
1.同步方法:
即有synchronized (同步,美 ['sɪŋkrənaɪzd] ) 修饰符修饰的方法。
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用给方法前,要获取内置锁,否则处于阻塞状态。
例:public synchronized getMoney(){}
注:synchronized修饰静态方法,如果调用该静态方法,将锁住整个类。
2.同步代码块
即有synchronized修饰符修饰的语句块,被该关键词修饰的语句块,将加上内置锁。实现同步。
例:synchronized(Object o ){}
同步是高开销的操作,因此尽量减少同步的内容。通常没有必要同步整个方法,同步部分代码块即可。
同步方法默认用this或者当前类class对象作为锁。
同步代码块可以选择以什么来加锁,比同步方法要更颗粒化,我们可以选择只同步会发生问题的部分代码而不是整个方法。
Q:sleep()和wait()的区别?
1、每个对象都有一个锁来控制同步访问,Synchronized关键字可以和对象的锁交互,来实现同步方法或同步块。sleep()方法正在执行的线程主动让出CPU(然后CPU就可以去执行其他任务),在sleep指定时间后CPU再回到该线程继续往下执行(注意:sleep方法只让出了CPU,而并不会释放同步资源锁!!!);wait()方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。(注意:notify的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说notify只是让之前调用wait的线程有权利重新参与线程的调度);
2、sleep()方法可以在任何地方使用;wait()方法则只能在同步方法或同步块中使用;
3、sleep()是线程类(Thread)的方法,调用会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复;wait()是Object的方法,调用会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才会进入锁池,不再次获得对象锁才会进入运行状态;
public class MultiThread {
private static class Thread1 implements Runnable{
@Override
public void run() {
//由于 Thread1和下面Thread2内部run方法要用同一对象作为监视器,如果用this则Thread1和Threa2的this不是同一对象
//所以用MultiThread.class这个字节码对象,当前虚拟机里引用这个变量时指向的都是同一个对象
synchronized(MultiThread.class){
System.out.println("enter thread1 ...");
System.out.println("thread1 is waiting");
try{
//释放锁有两种方式:(1)程序自然离开监视器的范围,即离开synchronized关键字管辖的代码范围
//(2)在synchronized关键字管辖的代码内部调用监视器对象的wait()方法。这里使用wait方法
MultiThread.class.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("thread1 is going on ...");
System.out.println("thread1 is being over!");
}
}
}
private static class Thread2 implements Runnable{
@Override
public void run() {
//notify方法并不释放锁,即使thread2调用了下面的sleep方法休息10ms,但thread1仍然不会执行
//因为thread2没有释放锁,所以Thread1得不到锁而无法执行
synchronized(MultiThread.class){
System.out.println("enter thread2 ...");
System.out.println("thread2 notify other thread can release wait status ...");
MultiThread.class.notify();
System.out.println("thread2 is sleeping ten millisecond ...");
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("thread2 is going on ...");
System.out.println("thread2 is being over!");
}
}
}
public static void main(String[] args) {
new Thread(new Thread1()).start();
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
new Thread(new Thread2()).start();
}
}
Q: 谈谈你对面向过程,面向对象,面向切面的理解
面向过程(Procedure Oriented)是一种以过程为中心的编程思想。这些都是以什么正在发生为主要目标进行编程,不同于面向对象的是谁在受影响。与面向对象明显的不同就是封装、继承、类。
面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。OOP 达到了软件工程的三个主要目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收信息、处理数据和向其它对象发送信息。
面向切面编程(Aspect Oriented Programming(AOP)),是一个比较热门的话题。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
切面,指的是项目模块中某些业务逻辑。面向切面编程通俗一点就是面向业务的一种编程思想(当然业务需要一定的共性),然后通过代理完成类的创建,极大程度的降低代码的重复,有效的提高了代码的重用率和开发效率。。。当然结合IOC(控制反转),将对象直接注入到代理类中,通过代理完成调用方法,那就更完美了
切面是横跨流程的,实现某个特定位置的功能,面向功能进行抽象。具体流程只需做引用即可,而不至于不同流程中冗余很多类似代码。