经过不断访问共享对象的状态,来判断是否知足了线程执行要求java
定义操做类MyObject,内部有一个volatile关键字修饰的list成员变量,使用volatile主要是为了让操做类对象的改变可让每一个线程都能感应到。代码以下:ide
package com.feng.example; import java.util.ArrayList; import java.util.List; public class MyObject { //此处必须是volatile,不然线程B中的object感应不到线程A中的object的变化 volatile private List<String> list = new ArrayList<String>(); public void add() { list.add("hahaha"); } public int size(){ //System.out.println("hahah"); return list.size(); } }
定义两个线程类MyThreadA 用于向MyObject对象中添加数据,MyThreadB用于在list中存在大于等于五个元素时退出函数
代码以下:工具
package com.feng.example; public class MyThreadA extends Thread{ private MyObject object; public MyThreadA(MyObject object) { this.object = object; } @Override public void run() { try { for(int i=0; i<10; i++) { System.out.println("添加了第"+i+"个元素"); object.add(); Thread.sleep(1000); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.feng.example; public class MyThreadB extends Thread { private MyObject object; public MyThreadB(MyObject object) { this.object = object; } @Override public void run() { // object.print(); try { while (true) { if (object.size() >= 5) { System.out.println("有五个元素,线程B应该退出了"); // 终止线程 throw new InterruptedException(); } } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
测试类代码以下:测试
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); Thread a = new MyThreadA(object); Thread b = new MyThreadB(object); a.start(); b.start(); } }
分析:线程b在while(true)无限循环中不断的判断object对象的个数,若是超过5就会终止线程。线程b须要不断的去访问object对象的size方法才能知道object对象的状态。这就是轮询的方式,很是的浪费cpu资源。
this
执行结果以下:spa
添加了第0个元素 添加了第1个元素 添加了第2个元素 添加了第3个元素 添加了第4个元素java.lang.InterruptedException 有五个元素,线程B应该退出了 at com.feng.example.MyThreadB.run(MyThreadB.java:20) 添加了第5个元素 添加了第6个元素 添加了第7个元素 添加了第8个元素 添加了第9个元素
使用wait和notify来实现多个线程间的通讯。下面介绍一下这两个方法的注意事项:线程
(1)wait使线程进入阻塞队列,notify唤醒保持同一锁对象的wait阻塞线程code
(2)wait和notify都只能在synchronized语句块或者synchronized方法中使用,保证使用时是持有锁的,不然会抛出异常对象
(3)调用wait方法,直接释放锁,调用notify方法,执行完同步代码段后才释放锁
(4)线程被notify唤醒后进入就绪队列,从新竞争锁,并非直接获取锁对象
(5)notify方法只唤醒一个线程,若是有多个wait线程,随机唤醒一个,notifyAll是唤醒全部(前提仍是要有同一个锁对象)
(6)notify方法执行以后,若是没有wait阻塞线程,则直接忽略
(7)终止wait中的线程,抛出异常
(8)wait(long) 等待特定的时间,在特定时间内没有被唤醒将自动唤醒
wait和notify,notifyAll方法是object中的方法,全部的类都是继承于object,所以全部的类都有wait和notify,notifyAll方法。
使用等待/通知机制实现上述功能:
修改两个线程类MyThreadA与MyThreadB,代码以下:
package com.feng.example; public class MyThreadA extends Thread{ private MyObject object; public MyThreadA(MyObject object) { this.object = object; } @Override public void run() { try { synchronized(object) { for(int i=0; i<10; i++) { System.out.println("添加了第"+i+"个元素"); object.add(); if(object.size()==5) { object.notify(); System.out.println("通知已发出"); } Thread.sleep(1000); } } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.feng.example; public class MyThreadB extends Thread { private MyObject object; public MyThreadB(MyObject object) { this.object = object; } @Override public void run() { // object.print(); try { synchronized(object) { if(object.size() < 5) { System.out.println("线程b等待"); object.wait(); System.out.println("线程b被唤醒 "); } throw new InterruptedException(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
测试类代码修改以下:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); Thread a = new MyThreadA(object); Thread b = new MyThreadB(object); b.start(); try { Thread.sleep(50); //确保线程b先执行 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } a.start(); } }
分析:线程a,b启动,线程a先获取到object的锁,往object对象中添加元素,添加到五个时,发出notify通知,可是线程a并不释放锁,而是继续执行,执行完同步代码段以后释放锁,而后线程b获取到锁,终止线程b
运行结果以下:
线程b等待 添加了第0个元素 添加了第1个元素 添加了第2个元素 添加了第3个元素 添加了第4个元素 通知已发出 添加了第5个元素 添加了第6个元素 添加了第7个元素 添加了第8个元素 添加了第9个元素 线程b被唤醒 java.lang.InterruptedException at com.feng.example.MyThreadB.run(MyThreadB.java:25)
上述程序须要保证是notify发生在wait以后,若是notify先发生,那么wait将永远等待
上述实验也证实了执行notify后是不释放锁的,执行完同步代码块以后才能释放锁。
观察下面代码,下面代码中的MyObject类为上例中的MyObject类
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); try { object.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
分析:object.wait()必需要有锁对象才可以调用,所以,这里的调用是不对的,运行会抛出非法的监视器状态违例
notify同理,这里就不演示了。执行结果以下:
Exception in thread "main" java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:485) at com.feng.example.ThreadTest.main(ThreadTest.java:13)
修改就是添加synchronized语句块,修改以下:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); try { synchronized(object) { object.wait(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
notify执行完同步代码块以后才能释放锁已经在2.1中证明了。下面验证wait直接释放锁。
修改测试类代码:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); Thread a = new MyThreadB(object); Thread b = new MyThreadB(object); b.start(); a.start(); } }
运行结果以下:
线程b等待 线程b等待
能够证明wait执行后直接释放锁
修改测试类:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); Thread a = new MyThreadB(object); a.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } a.interrupt(); } }
运行结果以下:
线程b等待 java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:485) at com.feng.example.MyThreadB.run(MyThreadB.java:22)
修改MyThreadB类:
package com.feng.example; public class MyThreadB extends Thread { private MyObject object; public MyThreadB(MyObject object) { this.object = object; } @Override public void run() { // object.print(); try { synchronized(object) { if(object.size() < 5) { System.out.println("线程b等待"+System.currentTimeMillis()); object.wait(1000); System.out.println("线程b被唤醒 "+System.currentTimeMillis()); } } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
修改测试类代码:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); Thread a = new MyThreadB(object); a.start(); } }
运行结果以下:
线程b等待1449803493388 线程b被唤醒 1449803494402
此示例中并无notify方法唤醒线程a,是等待1s以后自动唤醒。
定义操做类:
package com.feng.example; import java.util.ArrayList; import java.util.List; public class MyObject { //此处必须是volatile,不然线程B中的object感应不到线程A中的object的变化 volatile private List<String> list = new ArrayList<String>(); public void add() { list.add("hahaha"); } public int size() { return list.size(); } public void sub(){ //System.out.println("hahah"); list.remove(0); } }
定义两个线程类,线程B添加元素,线程A删除元素
package com.feng.example; public class MyThreadA extends Thread { private MyObject object; public MyThreadA(MyObject object) { this.object = object; } @Override public void run() { try { synchronized (object) { if (object.size() <= 0) { System.out.println("等待===="+Thread.currentThread().getName()); object.wait(); System.out.println("被唤醒==="+Thread.currentThread().getName()); } object.sub(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.feng.example; public class MyThreadB extends Thread { private MyObject object; public MyThreadB(MyObject object) { this.object = object; } @Override public void run() { synchronized (object) { object.add(); System.out.println("添加一个元素"); object.notifyAll(); System.out.println("唤醒全部"); } } }
测试类代码:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); Thread b = new MyThreadB(object); Thread a = new MyThreadA(object); Thread c = new MyThreadA(object); a.start(); c.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } b.start(); } }
分析:a,c两个线程都是执行的减操做,object中没有数据,两个线程都阻塞,线程b执行添加操做,唤醒a,c线程,只添加了一个元素,却唤醒两个元素去执行减操做,后减的线程会出现越界。
执行结果以下:
等待====Thread-1 等待====Thread-2 添加一个元素 Exception in thread "Thread-1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.RangeCheck(ArrayList.java:547) at java.util.ArrayList.remove(ArrayList.java:387) at com.feng.example.MyObject.sub(MyObject.java:23) at com.feng.example.MyThreadA.run(MyThreadA.java:24) 唤醒全部 被唤醒===Thread-2 被唤醒===Thread-1
修改类MyThreadA的代码,在唤醒后都要从新检查一遍是否知足减的条件,将if修改成while
package com.feng.example; public class MyThreadA extends Thread { private MyObject object; public MyThreadA(MyObject object) { this.object = object; } @Override public void run() { try { synchronized (object) { while (object.size() <= 0) { System.out.println("等待===="+Thread.currentThread().getName()); object.wait(); System.out.println("被唤醒==="+Thread.currentThread().getName()); } object.sub(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
执行结果以下:
等待====Thread-1 等待====Thread-2 添加一个元素 唤醒全部 被唤醒===Thread-2 被唤醒===Thread-1 等待====Thread-1
主要用的输入输出流为PipedInputStream PipedOutputStream
定义两个操做类WriteData用于写数据, ReaderData用于读数据,代码以下:
package com.feng.example; import java.io.IOException; import java.io.PipedOutputStream; public class WriteData { public void WriteMethod(PipedOutputStream out) { try { System.out.println("write:"); for(int i=0; i< 10; i++) { String data = ""+i; out.write(data.getBytes()); System.out.print(data); } System.out.println(); out.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.feng.example; import java.io.IOException; import java.io.PipedInputStream; public class ReaderData { public void ReaderMethod(PipedInputStream in) { try { System.out.println("read:"); byte[] data = new byte[11]; int length = in.read(data); while (length != -1) { String newData = new String(data, 0, length); System.out.print(newData); length = in.read(data); } System.out.println(); in.close(); } catch (IOException e) { e.printStackTrace(); } } }
定义两个线程类MyThreadA调用写操做,MyThreadB调用读操做,代码以下:
package com.feng.example; import java.io.PipedOutputStream; public class MyThreadA extends Thread { private WriteData writeData; private PipedOutputStream out; public MyThreadA(WriteData writeData, PipedOutputStream out) { this.writeData = writeData; this.out = out; } @Override public void run() { writeData.WriteMethod(out); } }
package com.feng.example; import java.io.PipedInputStream; public class MyThreadB extends Thread { private ReaderData readerData; private PipedInputStream in; public MyThreadB(ReaderData readerData, PipedInputStream in) { this.readerData = readerData; this.in = in; } @Override public void run() { readerData.ReaderMethod(in); } }
测试类代码以下:
package com.feng.example; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; public class ThreadTest { /** * @param args */ public static void main(String[] args) { try { WriteData wd = new WriteData(); ReaderData rd = new ReaderData(); PipedOutputStream out = new PipedOutputStream(); PipedInputStream in = new PipedInputStream(); out.connect(in); Thread a = new MyThreadA(wd, out); Thread b = new MyThreadB(rd, in); b.start(); Thread.sleep(2000); a.start(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
分析:在测试类中定义管道输入流in管道输出流out,使用in.connect(out)或者out.connect(in)将输入输出流绑定。也就是输入流读取的是输出流写入的数据。程序首先执行线程b,线程b执行读操做,此时输出流并无写任何数据,所以线程b在in.read(data)处阻塞。线程a执行写操做后,线程b才获取到数据继续执行。
结果以下:
read: write: 0123456789 0123456789
主要就是将处理字节改成处理字符,将PipedInputStream改成PipedReader,将PipedOutputStream改成PipedWriter
修改后的操做类以下:
package com.feng.example; import java.io.IOException; import java.io.PipedWriter; public class WriteData { public void WriteMethod(PipedWriter out) { try { System.out.println("write:"); for(int i=0; i< 10; i++) { String data = ""+i; out.write(data); System.out.print(data); } System.out.println(); out.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.feng.example; import java.io.IOException; import java.io.PipedReader; public class ReaderData { public void ReaderMethod(PipedReader in) { try { System.out.println("read:"); char[] data = new char[11]; int length = in.read(data); while (length != -1) { String newData = new String(data, 0, length); System.out.print(newData); length = in.read(data); } System.out.println(); in.close(); } catch (IOException e) { e.printStackTrace(); } } }
线程类以下:
package com.feng.example; import java.io.PipedWriter; public class MyThreadA extends Thread { private WriteData writeData; private PipedWriter out; public MyThreadA(WriteData writeData, PipedWriter out) { this.writeData = writeData; this.out = out; } @Override public void run() { writeData.WriteMethod(out); } }
package com.feng.example; import java.io.PipedReader; public class MyThreadB extends Thread { private ReaderData readerData; private PipedReader in; public MyThreadB(ReaderData readerData, PipedReader in) { this.readerData = readerData; this.in = in; } @Override public void run() { readerData.ReaderMethod(in); } }
测试类以下:
package com.feng.example; import java.io.IOException; import java.io.PipedReader; import java.io.PipedWriter; public class ThreadTest { /** * @param args */ public static void main(String[] args) { try { WriteData wd = new WriteData(); ReaderData rd = new ReaderData(); PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(); out.connect(in); Thread a = new MyThreadA(wd, out); Thread b = new MyThreadB(rd, in); b.start(); Thread.sleep(2000); a.start(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
运行结果:
read: write: 0123456789 0123456789
join方法用于等待线程对象销毁,执行a.join()的线程阻塞,等待线程对象a执行完成后继续执行。
定义线程类:
package com.feng.example; public class MyThread extends Thread { @Override public void run() { try { System.out.println(Thread.currentThread().getName()+"===="+System.currentTimeMillis()); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"===="+System.currentTimeMillis()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
定义测试类:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { try { Thread a = new MyThread(); a.start(); a.join(); Thread.sleep(100); System.out.println("线程a执行完了"+System.currentTimeMillis()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
运行结果:
Thread-0====1449821481676 Thread-0====1449821483689 线程a执行完了1449821483799
从结果能够看出,main函数中最后的输出语句是在线程a执行完成后输出的。
在join过程当中,若是线程(执行join的线程好比上述的主线程)被中断,抛出异常
join(long) 等待有限时间,注意与sleep(long)的区别
ThreadLocal主要解决线程间变量的隔离性,它会为每个线程保存一份本身的值。在Struts中使用的就是ThreadLocal
查看下面代码:
package com.feng.example; public class ThreadTest { /** * @param args */ private static ThreadLocal tl = new ThreadLocal(); public static void main(String[] args) { if(tl.get() == null) { System.out.println("没有放过值"); tl.set("newData"); } System.out.println(tl.get()); System.out.println(tl.get()); } }
运行结果以下:
没有放过值 newData newData
说明,没有值时取出的为null
下面来验证ThreadLocal的隔离性,新建两个线程,两个线程向ThreadLocal中添加数据,观察数据会不会出现混乱。
定义工具类:
package com.feng.example; public class Tools { public static ThreadLocal t = new ThreadLocal(); }
定义两个线程类:
package com.feng.example; public class MyThreadA extends Thread { @Override public void run() { try { for (int i = 0; i < 10; i++) { Tools.t.set(Thread.currentThread().getName() +" "+ i); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"==="+Tools.t.get()); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.feng.example; public class MyThreadB extends Thread { @Override public void run() { try { for (int i = 0; i < 10; i++) { Tools.t.set(Thread.currentThread().getName() +" "+ i); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"==="+Tools.t.get()); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
测试类:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { Thread a = new MyThreadA(); Thread b = new MyThreadB(); a.start(); b.start(); } }
运行结果:
Thread-1===Thread-1 0 Thread-0===Thread-0 0 Thread-1===Thread-1 1 Thread-0===Thread-0 1 Thread-1===Thread-1 2 Thread-0===Thread-0 2 Thread-1===Thread-1 3 Thread-0===Thread-0 3 Thread-1===Thread-1 4 Thread-0===Thread-0 4 Thread-1===Thread-1 5 Thread-0===Thread-0 5 Thread-1===Thread-1 6 Thread-0===Thread-0 6 Thread-1===Thread-1 7 Thread-0===Thread-0 7 Thread-1===Thread-1 8 Thread-0===Thread-0 8 Thread-1===Thread-1 9 Thread-0===Thread-0 9
从结果中能够看出,数据并无混乱,能够得知t.get(),是根据执行这条语句的currentThread来提取的值,t.set()也是根绝currentThread来存的值,也就是说每个线程都会有一个数据的备份,都有本身的值,相互不会影响。
若是想修改初始值,能够重写ThreadLocal值的initialValue方法;
定义本身的类继承ThreadLocal
package com.feng.example; public class MyThreadLocal extends ThreadLocal{ @Override protected Object initialValue() { // TODO Auto-generated method stub return "本身设置的初始值"; } }
测试:
package com.feng.example; public class ThreadTest { /** * @param args */ private static MyThreadLocal t = new MyThreadLocal(); public static void main(String[] args) { System.out.println(t.get()); } }
执行结果:
本身设置的初始值
可见本身设置的值生效了
此类主要能够获取到父线程的值,(父线程并非继承关系的,是谁建立的谁就是父线程)
定义本身的ThreadLocal类继承InheritableThreadLocal类,重写initialValue方法,这里返回时间,主要看何时初始化的。从而判断是子线程初始化仍是父线程初始化的。
代码以下:
package com.feng.example; import java.util.Date; public class MyThreadLocal extends InheritableThreadLocal{ @Override protected Object initialValue() { // TODO Auto-generated method stub return new Date(); } }
定义工具类:
package com.feng.example; public class Tools { public static MyThreadLocal t = new MyThreadLocal(); }
定义线程类:
package com.feng.example; public class MyThreadA extends Thread { @Override public void run() { System.out.println(Tools.t.get()); } }
定义测试类:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { System.out.println(Tools.t.get()); Thread a = new MyThreadA(); a.start(); } }
运行结果:
Fri Dec 11 16:49:48 CST 2015 Fri Dec 11 16:49:48 CST 2015
可见main线程中和a线程中的值是同样的,时间同样说明子线程继承了main线程中的值。
若是想在继承的时候修改,能够重写childValue方法
修改自定义ThreadLocal类,代码以下:
package com.feng.example; import java.util.Date; public class MyThreadLocal extends InheritableThreadLocal{ @Override protected Object initialValue() { // TODO Auto-generated method stub return new Date(); } @Override protected Object childValue(Object parentValue) { // TODO Auto-generated method stub return parentValue +"==="+new Date(); } }
运行代码以下:
Fri Dec 11 16:51:46 CST 2015 Fri Dec 11 16:51:46 CST 2015===Fri Dec 11 16:51:46 CST 2015
若是父线程中的值修改了,那么子线程获取的是在取值时候的值。