java线程方面的知识

Posted hadoop_dev

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java线程方面的知识相关的知识,希望对你有一定的参考价值。

java中单继承,多实现的:

若为多继承,那么当多个父类中有重复的属性或者方法时,子类的调用结果会含糊不清,因此用了单继承。

为什么是多实现呢?

通过实现接口拓展了类的功能,若实现的多个接口中有重复的方法也没关系,因为实现类中必须重写接口中的方法,所以调用时还是调用的实现类中重写的方法。那么各个接口中重复的变量又是怎么回事呢? 

接口中,所有属性都是 static final修饰的,即常量,这个什么意思呢,由于JVM的底层机制,所有static final修饰的变量都在编译时期确定了其值,若在使用时,两个相同的常量值不同,在编译时期就不能通过。

一、进程和线程的基础知识

1、进程和线程的概念

进程:运行中的应用程序称为进程,拥有系统资源(cpu、内存)

线程:进程中的一段代码,一个进程中可以有多段代码。本身不拥有资源(共享所在进程的资源)

Java中,程序入口被自动创建为主线程,在主线程中可以创建多个子线程。

区别:

 1、是否占有资源问题

 2、创建或撤销一个进程所需要的开销比创建或撤销一个线程所需要的开销大。

 3、进程为重量级组件,线程为轻量级组件

 

多进程: 在操作系统中能同时运行多个任务(程序)

多线程: 在同一应用程序中有多个功能流同时执行

 

2、线程的主要特点

①、不能以一个文件名的方式独立存在在磁盘中;

②、不能单独执行,只有在进程启动后才可启动;

③、线程可以共享进程相同的内存(代码与数据)。

 

3、线程的主要用途

①、利用它可以完成重复性的工作(如实现动画、声音等的播放)。

②、从事一次性较费时的初始化工作(如网络连接、声音数据文件的加载)。

③、并发执行的运行效果(一个进程多个线程)以实现更复杂的功能

 

4、多线程(多个线程同时运行)程序的主要优点

①、可以减轻系统性能方面的瓶颈,因为可以并行操作;

②、提高CPU的处理器的效率,在多线程中,通过优先级管理,可以使重要的程序优先操作,提高了任务管理的灵活性;另一方面,在多CPU系统中,可以把不同的线程在不同的CPU中执行,真正做到同时处理多任务。

java中创建一个线程有两种方式:

1、继承Thread类,重写run()方法,然后直接new这个对象的实例,创建一个线程的实例。然后调用start()方法启动线程

2、实现Runnable接口,重写run()方法,然后调用new Thread(runnable)的方式创建一个线程,然后调用start()方法启动线程

 

注意:

 

①、在继承Thread的方式中,可以使用getName()方法,来获得当前线程的名字,这是因为在Thread类中,有这个方法。可是在实现Runnable方式中,却不可以使用this.getName(),因为Runnable接口没有这个方法(可以看出来,因为我们没有提示我们需要重写这个方法),所以只能通过Thread的静态方法Thread.currentThread()取得当前的Thread对象,在调用getName()方法,来取得当前线程的名字。

②、对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称(和签名)。因此,在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程。只有调用start()方法才会启动新线程。

3、两种方式的对比

采用实现Runnable接口方式的多线程具有优势,一般都会使用这种方式:

1、线程类只是实现了Runnable接口,还可以继承其他类。

2、在这种方式下,可以多个线程共享一个Runnable target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。

 

notice: yeild和sleep不会释放锁,wait会释放锁.

 

 

线程的生命周期和状态控制:

说明:destory  stop 暴力停止线程的方法不安全 ,destory停止线程的时候不会释放线程的所有资源锁,其他的线程依旧阻塞,如果有一个线程想要获取该锁,就会死锁。stop()方法会强制释放所有的锁,使得程序继续运行,但是不安全。

suspend被弃用的原因是因为它会造成死锁。suspend方法和stop方法不一样,它不会破换对象和强制释放锁,相反它会一直保持对锁的占有,一直到其他的线程调用resume方法,它才能继续向下执行。

假如有A,B两个线程,A线程在获得某个锁之后被suspend阻塞,这时A不能继续执行,线程B在或者相同的锁之后才能调用resume方法将A唤醒,但是此时的锁被A占有,B不能继续执行,也就不能及时的唤醒A,此时A,B两个线程都不能继续向下执行而形成了死锁。这就是suspend被弃用的原因

如何正确的终止一个线程:可以在主线程中设置一个flag值,将flag置为false,即可停止子线程的运行.

线程的sleep方法和线程的yield方法区别:

① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;

 
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;


③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常; 


④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性.

 
其中  sleep和yield方法都是线程的静态方法,为什么要设置为静态方法:

原因:如果是实例方法,就可以实现在一个线程里随时让其它线程在执行中休眠,而且这种休眠是不放弃锁的。那么这种简单粗暴就很容易引起混乱,因此目前这种机制其实就是一种限制:你可以选择给自己吃安眠药,而不能随便给正在工作的别人吃安眠药。
 

 http://www.cnblogs.com/tk55/p/6875503.html

http://blog.csdn.net/lonelyroamer/article/details/7949969

 

Thread_wait、notify、notifyAll的使用方法

wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态。

这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。
如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
•如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。

其中wait方法有三个over load方法:
wait()
wait(long)
wait(long,int)

wait方法通过参数可以指定等待的时长。如果没有指定参数,默认一直等待直到被通知。

 

另外补充一些摘至其他博客的文章:

在多线程的情况下,由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。

   wait与notify是java同步机制中重要的组成部分。结合与synchronized关键字使用,可以建立很多优秀的同步模型。
  synchronized(this){ }等价于publicsynchronized void method(){.....}
   同步分为类级别和对象级别,分别对应着类锁和对象锁。类锁是每个类只有一个,如果static的方法被synchronized关键字修饰,则在这个方法被执行前必须获得类锁;对象锁类同。
   首先,调用一个Object的wait与notify/notifyAll的时候,必须保证调用代码对该Object是同步的,也就是说必须在作用等同于synchronized(obj){......}的内部才能够去调用obj的wait与notify/notifyAll三个方法,否则就会报错:
  java.lang.IllegalMonitorStateException:current thread not owner
  在调用wait的时候,线程自动释放其占有的对象锁,同时不会去申请对象锁。当线程被唤醒的时候,它才再次获得了去获得对象锁的权利。
  所以,notify与notifyAll没有太多的区别,只是notify仅唤醒一个线程并允许它去获得锁,notifyAll是唤醒所有等待这个对象的线程并允许它们去获得对象锁,只要是在synchronied块中的代码,没有对象锁是寸步难行的。其实唤醒一个线程就是重新允许这个线程去获得对象锁并向下运行。

   notifyAll,虽然是对每个wait的对象都调用一次notify,但是这个还是有顺序的,每个对象都保存这一个等待对象链,调用的顺序就是这个链的顺序。其实启动等待对象链中各个线程的也是一个线程,在具体应用的时候,需要注意一下。

  wait(),notify(),notifyAll()不属于Thread类,而是属于Object基础类,也就是说每个对像都有wait(),notify(),notifyAll()的功能。因为都个对像都有锁,锁是每个对像的基础,当然操作锁的方法也是最基础了。

wait():

等待对象的同步锁,需要获得该对象的同步锁才可以调用这个方法,否则编译可以通过,但运行时会收到一个异常:IllegalMonitorStateException。

调用任意对象的 wait() 方法导致该线程阻塞,该线程不可继续执行,并且该对象上的锁被释放。

notify():

唤醒在等待该对象同步锁的线程(只唤醒一个,如果有多个在等待),注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

调用任意对象的notify()方法则导致因调用该对象的 wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

notifyAll():

唤醒所有等待的线程,注意唤醒的是notify之前wait的线程,对于notify之后的wait线程是没有效果的。

 

通常,多线程之间需要协调工作:如果条件不满足,则等待;当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。

例如:
  synchronized(obj) {
  while(!condition) {
  obj.wait();
  }
  obj.doSomething();
  }
  
  当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。
  在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A :
  
  synchronized(obj) {
  condition = true;
  obj.notify();
  }
  
  需要注意的概念是:
  
  # 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj){...} 代码段内。

  
  # 调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj){...} 代码段内唤醒A。
  
  # 当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
  
  #如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
  
  #obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
  
  # 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。
  

谈一下synchronized和wait()、notify()等的关系:

1.有synchronized的地方不一定有wait,notify

2.有wait,notify的地方必有synchronized.这是因为wait和notify不是属于线程类,而是每一个对象都具有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。

另外,注意一点:如果要把notify和wait方法放在一起用的话,必须先调用notify后调用wait,因为如果调用完wait,该线程就已经不是currentthread了。

以上是关于java线程方面的知识的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程一些基础知识

程序员Java架构师多线程面试题和回答解析

深入理解 Java 多线程核心知识:跳槽面试必备

深入理解 Java 多线程核心知识:跳槽面试必备

java多线程知识点概述

Java之多线程