线程基本知识

概念:线程就是轻量级的进程,是程序执行的最小单位。

tip1:线程的五种状态:
1、新建   
  新建了一个线程
2、可运行 
  线程的start()方法被调用,该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权
3、运行状态
  获取CPU时间片,执行代码块
4、阻塞
  (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
  (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
  (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
5、死亡
  死亡(DEAD):线程run()、main() 方法执行结束


tip2:线程的创建

继承Thread和实现Runnable接口【默认的Thread.run()就是调用内部的Runnable接口】

tip3:线程的关闭

Thread的stop()方法

不推荐,原因是stop方法在结束线程时,会立刻是否这个线程所持有的锁。如果写线程写到一半,强行终止,对象就会被写坏,同时,由于锁已经释放,另一个等待该锁的读线程就读到了这个对象。

扩展知识:

一、JDK、JRE和JVM三者之间关系:  

1、所有的JAVA程序都会被编译成class文件,由虚拟机JVM和操作系统交互
2、JVM需要调用解释所需要的类库lib才可以执行class文件,JVM+lib=JRE即JAVA运行环境
3、JDK主要用于程序开发,最主要是编译器,包含了JRE,JAVA工具类和JAVA基础类库

 

二、怎么实现线程

实现Runnable接口或者继承Thread类来实现,当你打算多重继承时,优先选择实现Runnable

三、Thread.start()与Thread.run()有什么区别

调用start()后,线程会被放到等待队列,等待CPU调度。然后通过JVM,线程Thread会调用run()方法,执行本线程的线程体

四、为什么需要run()和start()方法,我们可以只用run()方法来完成任务吗

start()用来启动一个线程,当调用start()方法时,系统才会开启一个线程。run()只是一个线程里的一个函数,和多线程无关

 1 public synchronized void start() {
 2         if (threadStatus != 0)
 3             throw new IllegalThreadStateException();
 4         group.add(this);
 5 
 6         boolean started = false;
 7         try {
 8             start0();
 9             //private native void start0(),使用native修饰,Thread类要调用本地方法,要向JVM注册,这个方法调用了run方法
10             started = true;
11         } finally {
12             try {
13                 if (!started) {
14                     group.threadStartFailed(this);
15                 }
16             } catch (Throwable ignore) {
17             }
18         }
19     }

五、什么是ThreadLocal类,怎么使用它

通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改

六、什么时候抛出InvalidMonitorStateException异常,为什么

调用wait()/notify()/notifyAll()中的任何一个方法时,如果当前线程没有获得该对象 的锁,那么就会抛出IllegalMonitorStateException的异常

七、多线程中sleep、supend和wait区别

共同点:都会阻塞当前线程

不同点:

  1、sleep不会交出任何已获得的对象锁,是Thread类的方法,是线程用来控制自身流程的,调用此方法要捕捉InterruptedException异常

  2、wait会交出调用wait对象的对象锁。是Object类中定义的方法,用来线程间的通信,而且wait存在notify方法来唤醒调用wait的线程。

  3、suspend,使线程进入停滞状态,除非收到resume消息,否则该线程不会变回可执行状态

八、多线程中wait/notify区别

1、wait() 与 notify/notifyAll 方法必须在同步代码块中使用

  wait() 与 notify/notifyAll() 是Object类的方法,在执行两个方法时,要先通过synchronized['sɪŋkrənaɪzd]获得锁

2、notify/notifyAll()执行后,并不立即释放锁,而是要等到执行完临界区中代码后,再释放

3、多线程中测试某个条件的变化用 if 还是用 while?

List中没有数据了,再还是有线程去执行删除数据的操作。因此,需要用while循环来判断条件的变化,而不是用if

九、在静态方法上使用同步时会发生什么事

Synchronized修饰静态方法,实际上是对该类对象加锁,俗称“类锁”

答案:http://blog.csdn.net/u010842515/article/details/65443084

十、Thread的join方法

1、Thread类中的join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行执行

2、join方法必须在线程start方法调用之后调用才有意义

3、join方法的原理就是调用相应线程的wait方法进行等待操作的,例如A线程中调用了B线程的join方法,则相当于在A线程中调用了B线程的wait方法,当B线程执行完(或者到达等待时间),B线程会自动调用自身的notifyAll方法唤醒A线程,从而达到同步的目的

十一、死锁

形成原因:循环等待,多线程申请多资源,形成了循环等待

1、互斥:进程在某一时间内独占资源;
2、持有:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
3、不可剥夺:进程已获得资源,在末使用完之前,不能强行剥夺;
4、环形等待:若干进程之间形成一种头尾相接的循环等待资源关系;

解决方法:

1、针对2和3,引入事务机制,将所有上锁操作均作为事务对待,一旦开始上锁,即确保全部操作均可回退,同时通过锁管理器检测死锁,并剥夺资源

2、针对4,约定上锁的顺序必须一致

3、在尝试获取锁的时候加一个超时时间

4、死锁检测机制

每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。
当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。

 

知识补充:

一、Runnable接口和Callable接口的区别

1、两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;
2、Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

注意点:
Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果。

二、Volatile关键字的作用

使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据

 

posted @ 2018-03-12 12:00  不是植物  阅读(184)  评论(0编辑  收藏  举报