JAVA 多线程模型详解


JAVA 多线程模型详解 @Daiven 线程与进程 线程是一个独立运行的 1)线程与进程的区别:一个进程就是一个程序(任务处理器),它有独立的内存 空间,进程间(就是不同程序间所以他们是完成独立的代码和空间只是初始复制 过来时所有内容与本体一致,当然此处程序被允许有父子关系,也就是父子进程) 间通讯(传数据)一般比较困难,通常是一种信号机制或消息机制; 线程是依托于一个进程(程序)的,共享空间却独立代码。 2)线程原理:线程共享空间却独立代码,这是怎么实现的呢?原理就是 CPU 小 时间片调度替换线程来实现的。CPU 的运算时间被分为非常小的时间片,而后操 作系统(Win/Unix)提供 CPU 的调度算法,它们的算法我们是无法参与(只有优先 级),而后 CPU 的时间片分给不同的代码段(非连续的)再加以保护现场/恢复现 场机制,就产生不同执行段,而从宏观来看时间片足够小,我们就认为多个线程 好像同时在运行。 线程的创建 1) 创建方法 1 ClassA继承java.lang.Thread 类,则可调用ClassA对象的start()方法从而启动线程。 比如: 在线程 1 中,new ClassA()创建对象 ClassA,通过调用对象 ClassA 的 start()方 法,则线程 2 启动,并且线程 2 的入口为 ClassA.run()方法,然后,线程 1 与线 程 2 各自运行。 ClassA extend Thread start() sleep() Main() ① ② ③ 线程2 线程1 ④ ClassB wait() ……… ……… ③ run(){ …… } notify() ⑤ method(){ ……… } 开启新线程 注:不同的颜色线条表示不同的线程。 示例代码: class ClassA extends Thread { ClassA(String name){ //设置线程的name super.setName(name); } public void run() { System.out.println(super.getName()+" is running"); System.out.println(super.getName()+" over!"); } public static void main(String args[]){ ClassA thread1 = new ClassA("thread1"); thread1.start(); } } 运行结果: thread1 is running thread1 over! 2) 创建方法 2 ClassA 实现了 java.lang.Runnable 接口,并实现了该接口中的 run()方法,然后将 ClassA 作为参数创建一个 Thread 对象,并调用 start()方法启动新线程。比如: 在线程 1 中,通过将 ClassA 作为参数创建一个线程对象 Thread thread2 = new Thread(ClassA),调用 thread2 对象的 start()方法从而启动线程 2。 Interface:Runnable ClassA implements Runnable static sleep() static yield() Main() ① ② 线程2 线程1 ③ ClassB wait() ……… ……… ② run(){ …… } notify() ④ method(){ ……… } new Thread(new ClassA()) .start() ……… 开启新线程 注:不同的颜色线条表示不同的线程。 示例代码: class ClassA implements Runnable { public void run() { System.out.println("new thread is running"); System.out.println("new thread over!"); } public static void main(String args[]){ ClassA classA = new ClassA(); Thread thread1 = new Thread(classA); thread1.start(); } } 运行结果: new thread is running new thread over! 线程的阻塞与唤醒 wait()、notify()、nofityAll() 从概念上讲,线程是无法像汽车或者机器一样被控制的,就向电或水一样,无法控制电和水 停止,它们总是从高往低运行或流动。要想使其停止,只能是在其流动或运行的道路上,设 置路障或切断前进的道路。如何切断执行的路呢?可以通过对象的 wait()、notify()、nofityAll() 这几个方法实现。 首先,wait()、notify()、nofityAll()是对象的方法(注:线程只是 CPU 执行的顺序,线程不是 一个对象,所以它是没有方法的)。因此,当一个线程执行到该对象的 wait()方法时,该线 程运行的道路被阻塞,即不能继续往下执行。哪什么时候该线程再执行呢?当另一个线程调 用相同对象(必需是同一个对象)的 notify()/nofityAll()时,这时线程被唤醒,道路重新打开, 这时线程才能往下执行。 如下图所示: ① 线程 2 在运行,并且调用了 ClassB 对象的 wait()方法; ② 线程 2 不能继续向下运行,被阻塞;(具体线程如何阻塞是后台进行的) ③ 这时,线程 1 获得 ClassB 对象,并调用 ClassB 对象的 notify()、nofityAll()方法 ④ 后台唤醒被阻塞的线程 2;(即线程 2 被切断的道路重新开放) ⑤ 线程 1 继续向下执行; ⑥ 线程 2 继续向下执行; 线程2 ① ClassB instance wait() 线程1 阻塞 ② 唤醒被阻塞在该对象中的线程 ③ notify() / notifyAll() ⑥ …… ⑤ …… ④ 唤醒被阻塞在该 对象中的线程 线程暂停,不 占用CPU synchronized synchronized 注:不同的颜色线条表示不同的线程。 示例代码: public class ClassB implements Runnable{ public String name; ClassB(String name){ this.name = name; } synchronized public void run() { System.out.println(name+" is running"); try{ System.out.println(name+" wait()"); wait(); System.out.println(name+" running again"); }catch(Exception e){ e.printStackTrace(); } System.out.println(name+" over!"); } // 用于唤醒该对象中阻塞的线程 synchronized public void assign(){ notifyAll(); } public static void main(String[] args){ System.out.println("thread1 start"); ClassB classB = new ClassB("thread2"); Thread thread2 = new Thread(classB); thread2.start(); // sleep 5秒 try{ System.out.println("thread1 sleep 5s"); Thread.sleep(5 * 1000); }catch(Exception e){ e.printStackTrace(); } // notify thread2 System.out.println("thread1 notify thread2"); classB.assign(); System.out.println("thread1 over!"); } } 运行结果: thread1 start thread1 sleep 5s thread2 is running thread2 wait() thread1 notify thread2 thread2 running again thread2 over! thread1 over! 线程的同步 synchronized(结合 wait()说明) 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。哪如 何保证线程的同步呢?通过锁的概念来实现的。 锁只是一个概念,是针对具体 对象的,通过关键字 synchronized 实现。 怎 么 样 做 才能 被 锁住呢 ? 在某个类的代码块中或方法前面增加关键字 synchronized ,这时表示该内容执行时可以被锁住(即不能同时被执行),只能 表示该类的对象可以加锁,而不是该类被锁住。基于该类生成了一个实例对象, 线程执行到该对象的标识有 synchronized 代码块时,这时该对象才被锁住(仅锁 住标有 synchronized 的部分),其他的线程是不允许执行该对象的代码(标有 synchronized),当执行完 synchronized 代码块后,锁被释放,其他代码可以任意 执行。 注意:锁是针对对象的,不是针对类,因此,同一个类生成的两个实例,两个线 程分别执行两个实例的代码,则两个线程之间执行相互不受影响。 明白了同步、锁的概念后,就比较容易理解 synchronized 与 wait() 、notify()、 nitifyAll()之间的配合使用了。 wait()方法:首先,wait()方法必需要 synchronized 代码块中执行,不然会抛出错 误。当线程执行到某个对象的标有 synchronized 代码时,该对象被上锁,然后, 在 synchronized 代码块中执行到 wait()方法,则线程被阻塞,同时该对象的锁被 释放。 即其他线程可以执行该对象的标有 synchronized 的代码。 notify()、nitifyAll():当对象的锁被释放后,其他线程可以执行该对象的标有 synchronized 的代码块了,在 synchronized 代码块中执行 notify()、nitifyAll()方法 时(注:notify()、nitifyAll()方法必需在 synchronized 执行,否则会抛错),被阻塞 在该对象中的线程被唤醒,得以继续往下执行。特别注意,线程虽然被唤醒,但 还不能立即执行,必需得等到唤醒线程执行完 synchronized 代码块后,释放掉锁, 被唤醒的线程才能被执行。 如下图所示: ① 线程 2 在运行,开始执行对象 ClassB 的 synchronized 的代码块,这时对象 ClassB 被上锁,其他线程不能执行对象 ClassB 的标有 synchronized 的任意代码块。 ② 线程 2 执行到对象 ClassB 中的 wait()方法时,线程 2 被阻塞,同时锁被释放; 这时线程 1 才可以执行对象 ClassB 的 synchronized 代码块,并且得到对象 ClassB 的锁。 ③ 线程 1 执行到对象 ClassB 中 notify()、notifyAll()方法。 ④ 通过后台的机制,被阻塞的线程 2 这时候被唤醒。由于这时对象 ClassB 对象 的锁由线程 1 占有,线程 2 还不能被执行。 ⑤ 线程 1 执行完 synchronized 后面的代码,并释放对象 ClassB 的锁。 ⑥ 线程 2 得到对象 ClassB 的锁,然后继续执行后面的代码。 线程2 ① ClassB instance wait() 线程1 synchronized 阻塞 ② 唤醒被阻塞在该对象中的线程 ③ synchronized notify() / notifyAll() ⑥ …… ⑤ …… ④ 唤醒被阻塞在该 对象中的线程 …… Code …… ClassB对象被锁 Wait()方法执行 时,线程2阻塞, 锁被释放,这时 线程1才可以执行 对象中的 synchronized代码 注:不同的颜色线条表示不同的线程。 示例代码: public class ClassB implements Runnable{ public String name; ClassB(String name){ this.name = name; } synchronized public void run() { System.out.println(name+" is running"); try{ System.out.println(name+" is running util 5s start"); long time = System.currentTimeMillis(); while(System.currentTimeMillis() - time < 5*1000){ //System.out.println(name+" is running util 5s"); } System.out.println(name+" is running util 5s end"); System.out.println(name+" wait()"); wait(); System.out.println(name+" running again"); }catch(Exception e){ e.printStackTrace(); } System.out.println(name+" over!"); } // 用于唤醒该对象中阻塞的线程 synchronized public void assign(){ System.out.println("assign is called"); notifyAll(); } public static void main(String[] args){ System.out.println("thread1 start"); ClassB classB = new ClassB("thread2"); Thread thread2 = new Thread(classB); thread2.start(); // sleep 5秒 try{ System.out.println("thread1 sleep 1s"); Thread.sleep(5 * 1000); }catch(Exception e){ e.printStackTrace(); } // notify thread2 System.out.println("thread1 notify thread2"); classB.assign(); System.out.println(“thread1 over!”); } } 运行结果: thread1 start thread1 sleep 1s thread2 is running thread2 is running util 5s start thread2 is running util 5s end thread2 wait() thread1 notify thread2 assign is called // 直到thread2执行5s后,assign,notify才被调用 thread1 over! thread2 running again thread2 over! 线程 sleep() sleep()方法是 Thread 类的方法,该方法是使执行该方法的线程休眠。有多种使用 方法: 方法一:在任意线程中执行 Thread.sleep(xx),均可使当前线程休眠。 线程2 ① ClassB instance Thread.sleep(xx) 线程1 休眠 …… …… …… …… …… Code …… sleep使正在执行的 线程休眠。 在哪个线程中执行, 就是哪个线程休眠。 示例代码: public class ClassB implements Runnable{ public String name; ClassB(String name){ this.name = name; } synchronized public void run() { System.out.println(name+" is running"); try{ System.out.println(name+" is running util 5s start"); for(int i=0;i<5;i++){ // 线程休眠 Thread.sleep(1000); System.out.println(name+" is running "+(i+1)+"s"); } System.out.println(name+" is running util 5s end"); }catch(Exception e){ e.printStackTrace(); } System.out.println(name+" over!"); } // 用于唤醒该对象中阻塞的线程 synchronized public void assign(){ System.out.println("assign is called"); notifyAll(); } public static void main(String[] args){ System.out.println("thread1 start"); ClassB classB = new ClassB("thread2"); Thread thread2 = new Thread(classB); thread2.start(); System.out.println("thread1 end"); } } 运行结果: thread1 start thread1 end thread2 is running thread2 is running util 5s start thread2 is running 1s thread2 is running 2s thread2 is running 3s thread2 is running 4s thread2 is running 5s thread2 is running util 5s end thread2 over! 方法二:在 Thread 对象中执行,调用 Thread 对象的 sleep()方法,使当时线程休 眠。 线程2 ① ClassB extends Thread sleep(xx) 线程1 休眠 …… …… …… …… …… Code …… sleep使正在执行的 线程休眠。 在哪个线程中执行, 就是哪个线程休眠。 示例代码: public class ClassB extends Thread{ ClassB(String name){ super.setName(name); } synchronized public void run() { System.out.println(super.getName()+" is running"); try{ System.out.println(super.getName()+" is running util 5s start"); for(int i=0;i<5;i++){ // 线程休眠 sleep(1000); System.out.println(super.getName()+" is running "+(i+1)+"s"); } System.out.println(super.getName()+" is running util 5s end"); }catch(Exception e){ e.printStackTrace(); } System.out.println(super.getName()+" over!"); } public static void main(String[] args){ System.out.println("thread1 start"); ClassB classB = new ClassB("thread2"); classB.start(); System.out.println(“thread1 end”); } } 运行结果: thread1 start thread1 end thread2 is running thread2 is running util 5s start thread2 is running 1s thread2 is running 2s thread2 is running 3s thread2 is running 4s thread2 is running 5s thread2 is running util 5s end thread2 over! 特别注意:容易误解的是,在某一线程中调用 其它线程对象(即通过继承 Thread 类,生成的对象)的 sleep()方法时,实际上休眠的也是当前线程,而不是线程对 象生成的线程。 示例代码: public class ClassB extends Thread{ ClassB(String name){ super.setName(name); } synchronized public void run() { System.out.println(super.getName()+" is running"); try{ System.out.println(super.getName()+" is running util 5s start"); for(int i=0;i<5;i++){ // 线程休眠 sleep(1000); System.out.println(super.getName()+" is running "+(i+1)+"s"); } System.out.println(super.getName()+" is running util 5s end"); }catch(Exception e){ e.printStackTrace(); } System.out.println(super.getName()+" over!"); } public static void main(String[] args){ System.out.println("thread1 start"); ClassB classB = new ClassB("thread2"); classB.start(); try{ System.out.println("thread1 sleep 6s start"); classB.sleep(6 * 1000); //实际是线程 1 在休眠,不是线程 2 System.out.println(“thread1 sleep 6s end”); }catch(Exception e){ e.printStackTrace(); } System.out.println(“thread1 end”); } } 运行结果: thread1 start thread1 sleep 6s start thread2 is running thread2 is running util 5s start thread2 is running 1s thread2 is running 2s thread2 is running 3s thread2 is running 4s thread2 is running 5s thread2 is running util 5s end thread2 over! thread1 sleep 6s end thread1 end 线程 interrupt() 当外部线程对某线程调用了thread.interrupt()方法后,java语言的处理机制如下: 如果该线程处在可中断状态下,那么该线程会立即被唤醒,同时会收到一个 InterruptedException,可进行捕获。 概念:可中断状态:调用了 xx.wait(),或者 Selector.select(),Thread.sleep() 等特定会发生阻塞的 api 如果该线程处于可中断状态,同时阻塞在 io 上,对应的资源会被关闭。如果该 线程接下来不执行“Thread.interrupted()方法(不是 interrupt),那么该线程处理 任何 io 资源的时候,都会导致这些资源关闭。当然,解决的办法就是调用一下 interrupted(),不过这里需要程序员自行根据代码的逻辑来设定,根据自己的需 求确认是否可以直接忽略该中断,还是应该马上退出。 比如:线程 2 执行 sleep()或 wait()后,处于可中断状态,这时线程 1 调用 ClassB 的 interrupt()方法,使线程 2 阻塞中断,并且抛出 InterruptedException 异常。 线程2 ① ClassB extends Thread sleep(xx) / wait() 线程1 阻塞 …… …… Interrupt() …… …… Code …… 线程2阻塞 使线程2阻塞中断,并且抛出 InterruptedException异常,线程2捕获 示例代码: public class ClassB extends Thread{ ClassB(String name){ super.setName(name); } synchronized public void run() { System.out.println(super.getName() + " is running"); try { System.out.println(super.getName() + " wait()"); wait(); System.out.println(super.getName() + " running again"); } catch (Exception e) { System.out.println(super.getName() + " catch InterruptedException"); } System.out.println(super.getName() + " over!"); } public static void main(String[] args){ System.out.println("thread1 start"); ClassB classB = new ClassB("thread2"); classB.start(); try { for (int i = 0; i < 5; i++) { sleep(1000); System.out.println("thread1 is running " + (i + 1) + "s"); } System.out.println("thread1 interrupt thread2 start"); classB.interrupt(); // 使线程 2 阻塞中断,并抛出异常 System.out.println("thread1 interrupt thread2 end"); } catch (Exception e) { e.printStackTrace(); } System.out.println("thread1 end"); } } 运行结果: thread1 start thread2 is running thread2 wait() thread1 is running 1s thread1 is running 2s thread1 is running 3s thread1 is running 4s thread1 is running 5s thread1 interrupt thread2 start thread1 interrupt thread2 end thread1 end thread2 catch InterruptedException thread2 over! 比如:如果同一个 Thread 对象上,有两个正在执行的线程,并且均处于可中断 状态,这时线程 1 调用 ClassB 的 interrupt()方法,使线程 2、线程 3 的阻塞均中 断,并且抛出 InterruptedException 异常,但该异常只能被其中一个线程捕获。 线程2 ① ClassB extends Thread sleep(xx) / wait() 线程1 阻塞 …… …… Interrupt() …… …… Code …… 线程2阻塞 使线程2、线程3阻塞均中断,两个线程均 往下执行。同时,抛出InterruptedException 异常,但只能被其中一个线程捕获。 sleep(xx) / wait() 阻塞 …… …… Code …… 线程3阻塞 线程3 示例代码: public class ClassB extends Thread{ int i = 2; ClassB(String name){ super.setName(name); } synchronized public void run() { if(i == 2){ i++; m("thread2"); }else{ m("thread3"); } } public void m(String threadname){ System.out.println(threadname + " is running start"); try { wait(); } catch (Exception e) { System.out.println(threadname + " catch InterruptedException"); } System.out.println(threadname + " is running end"); } public static void main(String[] args){ System.out.println("thread1 start"); ClassB classB = new ClassB("thread2"); classB.start(); Thread thread3 = new Thread(classB); thread3.setName("thread3"); thread3.start(); try { sleep(2*1000); System.out.println("thread1 interrupt thread2 start"); classB.interrupt(); // 使线程中断,并抛出异常 System.out.println("thread1 interrupt thread2 end"); } catch (Exception e) { e.printStackTrace(); } System.out.println("thread1 end"); } } 运行结果: thread1 start thread2 is running start thread3 is running start thread1 interrupt thread2 start thread2 catch InterruptedException //只有线程2捕获了异常 thread2 is running end //两个线程均被唤醒 thread3 is running end //两个线程均被唤醒 thread1 interrupt thread2 end thread1 end 如果该线程处在不可中断状态下,就是没有调用上述 api,那么 java 只是设置一 下该线程的 interrupt 状态,其他事情都不会发生,如果该线程之后会调用行数 阻塞 API,那到时候线程会马会上跳出,并抛出 InterruptedException,接下来的 事情就跟第一种状况一致了。如果不会调用阻塞 API,那么这个线程就会一直执 行下去。除非你就是要实现这样的线程,一般高性能的代码中肯定会有 wait(), yield()之类出让 cpu 的函数,不会发生后者的情况。 例:线程 2 一直在执行代码,并且处于不可中断状态,线程 1 调用了 ClassB 的 interrupt()方法,这时线程 2 仍然继续执行。只是设置一下该线程的 interrupt 状 态。 线程2 ① ClassB extends Thread 线程1 …… …… Interrupt() …… …… Code …… 对线程2不起作用 示例代码: public class ClassB extends Thread{ ClassB(String name){ super.setName(name); } synchronized public void run() { System.out.println(super.getName() + " is running"); try { for(int i=1;i>0;i++){ System.out.println(super.getName() + " is running "+i+"s"); long time = System.currentTimeMillis(); while(System.currentTimeMillis() - time <1000){ } } } catch (Exception e) { System.out.println(super.getName() + " catch InterruptedException"); } System.out.println(super.getName() + " over!"); } public static void main(String[] args){ System.out.println("thread1 start"); ClassB classB = new ClassB("thread2"); classB.start(); try { for (int i = 0; i < 5; i++) { sleep(1000); } System.out.println("thread1 interrupt thread2 start"); classB.interrupt(); // 使线程 2 阻塞中断,并抛出异常,然而线程 2 处于不可 中断状态 System.out.println("thread1 interrupt thread2 end"); } catch (Exception e) { e.printStackTrace(); } System.out.println("thread1 end"); } } 运行结果: thread1 start thread2 is running thread2 is running 1s thread2 is running 2s thread2 is running 3s thread2 is running 4s thread2 is running 5s thread2 is running 6s thread1 interrupt thread2 start thread1 interrupt thread2 end thread1 end thread2 is running 7s thread2 is running 8s thread2 is running 9s thread2 is running 10s ….. // thread2 会一直执行下去,并没有被 interrupt
还剩22页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 2 人已下载

下载pdf

pdf贡献者

daivenpeng

贡献于2014-10-01

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf