线程2 Java多线程状态

GUI应用程序
   几乎所有的GUI应用程序都会用多线程。使用word来编辑一个非常大的文件,一个线程用来编辑,另一个线程用来做搜索。
多个客户端
   基本上网络上的服务器必须同时处理一个以上的客户端,不过,一定要在服务器这边的程序设计加入一个以上客户端的概念的话,程序会变得更复杂。此时,不妨准备一个当有客户端连接到服务器的时候,会自动出来迎接这个客户点的线程,这样一来, 服务器的程序就可以设计成好像只服务一个客户端

线程的暂时停止:
try{
    Thread.sleep(1000);
}catch(InterruptedException e){
}
sleep方法的调用被放在try-catch里面,这是因为sleep方法可能会抛出一个称为InterruptedException的异常。InterruptedException是用来取消线程处理时的异常。
“半路唤醒”被Thread sleep暂停的线程,则可利用interrupt方法


Synchronized实例方法和Synchronized阻挡
假设现在有一个类型如下的synchronized实例方法
synchronized void method(){
    …
}
在功能上和下面以synchronized阻挡为主的方法有异曲同工之妙。
void method(){
        s y nchronized(this){
            …
       }
}
换句话说synchronized方法是使用 this锁 去做线程的共享互斥。
this 指的是当前对象实例本身,所以,所有使用 synchronized(this) 方式的方法都共享同一把锁。
Synchronized类方法和Synchronized阻挡
假设现在有一个类型如下的synchronized的类方法,synchronized类方法有限制同时只能让一个线程执行。这部分和synchronized实例方法一样,但是两者是有不同的。
Class Something{
    static synchronized voidmethod(){
           …
     }
}
在功能上和下面以synchronized阻挡为主的方法有异曲同工之妙。
Class Something{
    static void method(){
       synchronized(Something.class) {
             …
         }
    }
}
换句话说synchronized的类方法是使用该类的 类对象 的锁去做线程的共享互斥。Something.class是对应 Something类的java.lang.Class类的实例。
如果是synchronized(this)是锁住整个类的实例对象,如果所有的锁都锁this,只要锁住一处所有地方都不能用了
private Object lock1 = new Object();
private Object lock2 = new Object();
public void test1(){
    synchronized(lock){//影响的只是访问锁lock的这个锁,如果再构造一个别的对象锁上,他们就互不影响,他们之间共享的不是同一把锁
        //互斥代码
    }
}
这里synchronized关键字拿到的锁是对象object的锁,所有需要这个对象的锁的方法都不能同时执行。这是最常用的高并发场景下要锁住某个方法所用的操作。 锁住同一个变量的方法块共享同一把锁


每个实例都有个线程的休息室wait set。
Wait方法——把线程放入wait set
使用wait方法时,线程便进入wait set,假设现在已经执行如下语句:obj.wait();
则目前的线程停止执行,进入实例obj的的wait set.这个操作成为: 线程在obj上wait().(这个obj不是线程而是对象)
如果实例方法还有如下的语句时:wait();
则其意义同:this.wait();
故执行wait的线程就会进入this的wait set.此时就变成了在this上wait.
如欲执行wait()方法,线程需获取锁定(这是规则)。但是当线程进入wait set时,已经释放了该实例的锁定。
Notify方法——从wait set拿出线程
使用notify()(通知)方法时,可以从wait set拿出一个线程。
obj.notify();(这个obj也是对象,不是线程)则从wait set里的线程中挑出一个,唤醒这个线程。被唤醒的线程便退wait set
线程A想紧接着wait之后的代码执行,但是现在线程B拿到了锁
Notify后的线程
    被notify唤醒的线程不是在notify后立即执行,因为在notify的那一刻,执行notify 的线程还握着锁定不放,所以其他线程无法获取该实例的锁定。
Notify如何选择线程
       假设执行notify方法时,wait set里面正在执行的线程不止一个。规格并没有注明此时该选择哪一个线程。究竟是选择等待线程里面的第一个,随机选择或是另以其他方式选择,则以java处理系统而异。  
notifyAll()方法——从wait set 拿出所有线程
使用notifyAll(通知全体)方法时,会将所有在waitset苦等的线程都拿出来。
obj.notifyAll()则会唤醒所有留在 实例obj的wait set里的线程。
而notifyAll();则其意义同this.notifyAll();故这个语句所在方法的实例(this)的wait set里的线程会全部放出来。
跟wait方法和notify方法一样,线程必须要获取要调用实例的锁定,才能调用notifyAll方法。
被唤醒的线程便开始去获取刚才wait时释放掉的锁定,那么现在这个锁定现在是在谁的手中呢? 没错,锁定就是在刚才执行notifyAll方法的程序手里,因此即使所有线程都退出了wait set,但他们仍然在去获得锁定的状态下,还是有阻挡 。要等到刚才执行notifyAll方法的线程释放出锁定后,其中一名幸运儿才会实际执行。
要是没有锁定呢
    若没有锁定的线程去调用wait,notify或notifyAll时,便会抛出异常java.lang.IllegalMonitorStateException.
调用notify方法还是notifyAll方法
   Notify方法和notifyAll方法两者非常相似,到底该用哪一个?老实说,这个选择有点难。选择notify的话,因为要唤醒的线程比较少,程序处理速度当然要比notifyAll略胜一筹。但是选择notify时,若这部分程序处理的不好,可能会有程序挂掉的危险性,一般说来,选择notifyAll所写出来的程序代码要比选择notify可靠。除非你能确定程序员对程序代码的意义和能力限度一清二楚,否则选择notifyAll应该比较稳扎稳打
wait,notify,notifyAll是Object类的方法
obj.wait()是把现在的线程放到obj的wait set
obj.notify()是从obj的wait set里唤醒一个线程
obj.notifyAll()是唤醒所有在obj的wait set里的线程
换句话说,把wait、notify、notifyAll三者均解释为对实例对象的wait set的操作,会比说他们是对线程的操作更贴切,由于所有实例都会有wait set,所以wait、notify、notifyAll才会是Object类的方法。
虽然三者不是Thread类固有的方法,不过,因为Object类是Java所有类的祖先类,所以wait、notify、notifyAll也是Thread的方法。

线程的基本概念、线程的基本状态以及状态之间的关系
从图中可以看出从执行状态出来后都不能直接回到执行状态,包括Thread.yield()/wait/sleep/io完成,首先回到可执行状态。
    
 
1. 新建( new ):新创建了一个线程对象。
2. 可运行( runnable ):线程对象创建后,其他线程(比如 main 线程)调用了该对象 的 start ()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取 cpu 的使用权 。
3. 运行( running ):可运行状态( runnable )的线程获得了 cpu 时间片( timeslice ) ,执行程序代码。
4. 阻塞( block ):阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice ,暂时停止运行。直到线程进入可运行( runnable )状态,才有 机会再次获得 cpu timeslice 转到运行( running )状态。阻塞的情况分三种:
Blocked(堵塞)状态:
同步锁;调用了sleep()和join()方法进入Sleeping状态;执行wait()方法进入Waiting状态,等待其他线程notify通知唤醒);
(一). 等待阻塞:运行( running )的线程执行 o . wait ()方法, JVM 会把该线程放 入等待队列( waitting queue )中。
(二). 同步阻塞:运行( running )的线程在获取对象的同步锁时(synchronized),若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池( lock pool )中。
(三). 其他阻塞: 运行( running )的线程执行 Thread . sleep ( long ms )或 t . join ()方法,或者发出了 I / O 请求时, JVM 会把该线程置为阻塞状态。当 sleep ()状态超时、 join ()等待线程终止或者超时、或者 I / O 处理完毕时,线程重新转入可运行( runnable )状态。
5. 死亡( dead ):线程 run ()、 main () 方法执行结束,或者因异常退出了 run ()方法,则该线程结束生命周期。死亡的线程不可再次复生。

1 java 中有几种方法可以实现一个线程?用什么关键字修饰同步方法 ? stop() suspend() 法为何不推荐使用?
答: 有两种实现方法,分别是继承 Thread 类与实现 Runnable 接口
synchronized 关键字修饰同步方法
反对使用 stop() ,是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。 suspend() 方法容易发生死锁。调用 suspend() 的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被 " 挂起 " 的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用 suspend() ,而应在自己的 Thread 类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用 wait() 命其进入等待状态。若标志指出线程应当恢复,则用一个 notify() 重新启动线程。
2 sleep()   wait()  有什么区别
答: sleep 是线程类( Thread )的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用 sleep 不会释放对象锁。
wait Object 类的方法,对此对象调用 wait 方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出 notify 方法(或 notifyAll )后本线程才进入对象锁定池准备获得对象锁进入运行状态。
3 、同步和异步有何异同,在什么情况下分别使用他们?举例说明。
答: 如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
4 、启动一个线程是用 run() 还是 start()?
答: 启动一个线程是调用 start() 方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由 JVM 调度并执行。这并不意味着线程就会立即运行。
5 、当一个线程进入一个对象的一个 synchronized 方法后,其它线程是否可进入此对象的其它 synchronized 方法 ?
答: 不能,一个对象的一个 synchronized 方法只能由一个线程访问。
6 、请说出你所知道的线程同步的方法。
答: wait(): 使一个线程处于等待状态,并且释放所持有的对象的 lock
sleep(): 使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉 InterruptedException 异常。
notify(): 唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且不是按优先级。
notityAll(): 唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
8 、简述 synchronized java.util.concurrent.locks.Lock 的异同 
答: 主要相同点: Lock 能完成 synchronized 所实现的所有功能主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。
synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值