多线程 程序
wait()和notify()示例
public class Client{ public static void main(String[] args) { Threada t1 = new Threada("t1"); synchronized (t1) { try { System.out.println(Thread.currentThread().getName() + "start t1"); t1.start(); System.out.println(Thread.currentThread().getName() + "wait"); t1.wait(); System.out.println(Thread.currentThread().getName() + "continue"); } catch (Exception e) { // TODO: handle exception } } } } class Threada extends Thread { public Threada(String name) { super(name); } @Override public void run() { synchronized (this) { System.out.println(Thread.currentThread().getName() + "call notify"); notify(); } } }
mainstart t1
mainwait
t1call notify
maincontinue
wait(long timeout)和notify()
public class Client{ public static void main(String[] args) { Threada t1 = new Threada("t1"); synchronized (t1) { try { System.out.println(Thread.currentThread().getName() + "start t1"); t1.start(); System.out.println(Thread.currentThread().getName() + "wait"); t1.wait(3000); System.out.println(Thread.currentThread().getName() + "continue"); } catch (Exception e) { // TODO: handle exception } } } } class Threada extends Thread { public Threada(String name) { super(name); } @Override public void run() { synchronized (this) { System.out.println(Thread.currentThread().getName() + "run"); while (true) { ; } } } }
main start t1 main call wait t1 run // 大约3秒之后...输出“main continue” main continue
wait() 和 notifyAll()
public class Client{ private static Object obj = new Object(); public static void main(String[] args) { Threada t1 = new Threada("t1"); Threada t2 = new Threada("t2"); Threada t3 = new Threada("t3"); t1.start(); t2.start(); t3.start(); try { System.out.println(Thread.currentThread().getName() + " sleep"); Thread.sleep(3000); } catch (Exception e) { // TODO: handle exception } synchronized (obj) { System.out.println(Thread.currentThread().getName() + " notify all"); obj.notifyAll(); } } } static class Threada extends Thread { public Threada(String name) { super(name); } @Override public void run() { synchronized (obj) { try { System.out.println(Thread.currentThread().getName() + " await"); obj.wait(); System.out.println(Thread.currentThread().getName() + " continue"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
main sleep t3 await t1 await t2 await main notify all t2 continue t1 continue t3 continue
yield() 线程让步 它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;
public class Client{ private static Object obj = new Object(); public static void main(String[] args) { Threada t1 = new Threada("t1"); Threada t2 = new Threada("t2"); t1.start(); t2.start(); } } class Threada extends Thread { public Threada(String name) { super(name); } @Override public synchronized void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i % 4 == 0) { Thread.yield(); } } } }
(01) wait()是让线程由“运行状态”进入到“等待(阻塞)状态”,而不yield()是让线程由“运行状态”进入到“就绪状态”。
(02) wait()是会线程释放它所持有对象的同步锁,而yield()方法不会释放锁。
sleep 让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。
wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。而sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”。
但是,wait()会释放对象的同步锁,而sleep()则不会释放锁。
public class Client{ private static Object obj = new Object(); public static void main(String[] args) { Threada t1 = new Threada("t1"); Threada t2 = new Threada("t2"); t1.start(); t2.start(); } static class Threada extends Thread { public Threada(String name) { super(name); } @Override public void run() { synchronized (obj) { try { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i % 4 == 0) { Thread.sleep(400); } } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
t1: 0 t1: 1 t1: 2 t1: 3 t1: 4 t1: 5 t1: 6 t1: 7 t1: 8 t1: 9 t2: 0 t2: 1 t2: 2 t2: 3 t2: 4 t2: 5 t2: 6 t2: 7 t2: 8 t2: 9
join() 的作用:让“主线程”等待“子线程”结束之后才能继续运行。
public class Father extends Thread { public void run() { Son s = new Son(); s.start(); s.join(); ... } } // 子线程 public class Son extends Thread { public void run() { ... } }
public class Client{ private static Object obj = new Object(); public static void main(String[] args) throws InterruptedException { Threada t1 = new Threada("t1"); t1.start(); t1.join(); System.out.println(Thread.currentThread().getName() + " final"); } static class Threada extends Thread { public Threada(String name) { super(name); } @Override public void run() { // synchronized (obj) { System.out.println(Thread.currentThread().getName() + " start"); for (int i = 0; i < 1000000; i++) { } System.out.println(Thread.currentThread().getName() + " final"); } // } } }
t1 start
t1 final
main final
interrupt() interrupt()的作用是中断本线程。
本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。
如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。
如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。
如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。
中断一个“已终止的线程”不会产生任何操作。
终止处于“阻塞状态”的线程
@Override public void run() { try { while (true) { // 执行任务... } } catch (InterruptedException ie) { // 由于产生InterruptedException异常,退出while(true)循环,线程终止! } }
在while(true)中不断的执行任务,当线程处于阻塞状态时,调用线程的interrupt()产生InterruptedException中断。中断的捕获在while(true)之外,这样就退出了while(true)循环!
注意:对InterruptedException的捕获务一般放在while(true)循环体的外面,这样,在产生异常时就退出了while(true)循环。否则,InterruptedException在while(true)循环体之内,就需要额外的添加退出处理。
终止处于“运行状态”的线程
@Override public void run() { while (!isInterrupted()) { // 执行任务... } }
isInterrupted()是判断线程的中断标记是不是为true。当线程处于运行状态,并且我们需要终止它时;可以调用线程的interrupt()方法,使用线程的中断标记为true,即isInterrupted()会返回true。此时,就会退出while循环。
注意:interrupt()并不会终止处于“运行状态”的线程!它会将线程的中断标记设为true。
@Override public void run() { try { // 1. isInterrupted()保证,只要中断标记为true就终止线程。 while (!isInterrupted()) { // 执行任务... } } catch (InterruptedException ie) { // 2. InterruptedException异常保证,当InterruptedException异常产生时,线程被终止。 } }
class MyThread extends Thread { 3 4 private volatile boolean flag= true; 5 public void stopTask() { 6 flag = false; 7 } 8 9 public MyThread(String name) { 10 super(name); 11 } 12 13 @Override 14 public void run() { 15 synchronized(this) { 16 try { 17 int i=0; 18 while (flag) { 19 Thread.sleep(100); // 休眠100ms 20 i++; 21 System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i); 22 } 23 } catch (InterruptedException ie) { 24 System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException."); 25 } 26 } 27 } 28 }
每个线程都有一个优先级。“高优先级线程”会优先于“低优先级线程”执行。每个线程都可以被标记为一个守护进程或非守护进程。在一些运行的主线程中创建新的子线程时,子线程的优先级被设置为等于“创建它的主线程的优先级”,当且仅当“创建它的主线程是守护线程”时“子线程才会是守护线程”。
当Java虚拟机启动时,通常有一个单一的非守护线程(该线程通过是通过main()方法启动)。JVM会一直运行直到下面的任意一个条件发生,JVM就会终止运行:
(01) 调用了exit()方法,并且exit()有权限被正常执行。
(02) 所有的“非守护线程”都死了(即JVM中仅仅只有“守护线程”)。
每一个线程都被标记为“守护线程”或“用户线程”。当只有守护线程运行时,JVM会自动退出。
java 中有两种线程:用户线程和守护线程。可以通过isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。java 中有两种线程:用户线程和守护线程。可以通过isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。
生产/消费者问题是个非常典型的多线程问题,涉及到的对象包括“生产者”、“消费者”、“仓库”和“产品”。他们之间的关系如下:
(01) 生产者仅仅在仓储未满时候生产,仓满则停止生产。
(02) 消费者仅仅在仓储有产品时候才能消费,仓空则等待。
(03) 当消费者发现仓储没产品可消费时候会通知生产者生产。
(04) 生产者在生产出可消费产品时候,应该通知等待的消费者去消费。
复制代码 1 // Demo1.java 2 // 仓库 3 class Depot { 4 private int capacity; // 仓库的容量 5 private int size; // 仓库的实际数量 6 7 public Depot(int capacity) { 8 this.capacity = capacity; 9 this.size = 0; 10 } 11 12 public synchronized void produce(int val) { 13 try { 14 // left 表示“想要生产的数量”(有可能生产量太多,需多此生产) 15 int left = val; 16 while (left > 0) { 17 // 库存已满时,等待“消费者”消费产品。 18 while (size >= capacity) 19 wait(); 20 // 获取“实际生产的数量”(即库存中新增的数量) 21 // 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库) 22 // 否则“实际增量”=“想要生产的数量” 23 int inc = (size+left)>capacity ? (capacity-size) : left; 24 size += inc; 25 left -= inc; 26 System.out.printf("%s produce(%3d) --> left=%3d, inc=%3d, size=%3d\n", 27 Thread.currentThread().getName(), val, left, inc, size); 28 // 通知“消费者”可以消费了。 29 notifyAll(); 30 } 31 } catch (InterruptedException e) { 32 } 33 } 34 35 public synchronized void consume(int val) { 36 try { 37 // left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费) 38 int left = val; 39 while (left > 0) { 40 // 库存为0时,等待“生产者”生产产品。 41 while (size <= 0) 42 wait(); 43 // 获取“实际消费的数量”(即库存中实际减少的数量) 44 // 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”; 45 // 否则,“实际消费量”=“客户要消费的数量”。 46 int dec = (size<left) ? size : left; 47 size -= dec; 48 left -= dec; 49 System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", 50 Thread.currentThread().getName(), val, left, dec, size); 51 notifyAll(); 52 } 53 } catch (InterruptedException e) { 54 } 55 } 56 57 public String toString() { 58 return "capacity:"+capacity+", actual size:"+size; 59 } 60 } 61 62 // 生产者 63 class Producer { 64 private Depot depot; 65 66 public Producer(Depot depot) { 67 this.depot = depot; 68 } 69 70 // 消费产品:新建一个线程向仓库中生产产品。 71 public void produce(final int val) { 72 new Thread() { 73 public void run() { 74 depot.produce(val); 75 } 76 }.start(); 77 } 78 } 79 80 // 消费者 81 class Customer { 82 private Depot depot; 83 84 public Customer(Depot depot) { 85 this.depot = depot; 86 } 87 88 // 消费产品:新建一个线程从仓库中消费产品。 89 public void consume(final int val) { 90 new Thread() { 91 public void run() { 92 depot.consume(val); 93 } 94 }.start(); 95 } 96 } 97 98 public class Demo1 { 99 public static void main(String[] args) { 100 Depot mDepot = new Depot(100); 101 Producer mPro = new Producer(mDepot); 102 Customer mCus = new Customer(mDepot); 103 104 mPro.produce(60); 105 mPro.produce(120); 106 mCus.consume(90); 107 mCus.consume(150); 108 mPro.produce(110); 109 } 110 }
public class Client{ } class Depot { private int size; private int capacty; private Lock lock; private Condition fullCondition; private Condition emptyCondition; public Depot(int capacty) { super(); this.capacty = capacty; this.size = 0; this.lock = new ReentrantLock(); this.fullCondition = lock.newCondition(); this.emptyCondition = lock.newCondition(); } public void produce(int val) { lock.lock(); try { int left = val; while (left > 0) { while (size >= capacty) { fullCondition.await(); int inc = (size + left) > capacty ? capacty - size : left; size += inc; left -= inc; System.out.printf("%s produce(%3d) --> left=%3d, inc=%3d, size=%3d\n", Thread.currentThread().getName(), val, left, inc, size); emptyCondition.signal(); } } } catch (Exception e) { // TODO: handle exception } finally { lock.unlock(); } } public void consume(int val) { lock.lock(); try { int left = val; while (left > 0) { while (size <= 0) { emptyCondition.await(); int dec = (size >= left) ? left : size; size -= dec; left -= dec; System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", Thread.currentThread().getName(), val, left, dec, size); fullCondition.signal(); } } } catch (Exception e) { // TODO: handle exception }finally { lock.unlock(); } } } class Producer { private Depot depot; public Producer(Depot depot) { super(); this.depot = depot; } public void produce(final int val) { new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub depot.produce(val); } }).start(); } } class Consumer { private Depot depot; public Consumer(Depot depot) { super(); this.depot = depot; } public void consume(final int val) { new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub depot.consume(val); } }).start(); } }
(01) Producer是“生产者”类,它与“仓库(depot)”关联。当调用“生产者”的produce()方法时,它会新建一个线程并向“仓库”中生产产品。
(02) Customer是“消费者”类,它与“仓库(depot)”关联。当调用“消费者”的consume()方法时,它会新建一个线程并消费“仓库”中的产品。
(03) Depot是“仓库”类,仓库中记录“仓库的容量(capacity)”以及“仓库中当前产品数目(size)”。
“仓库”类的生产方法produce()和消费方法consume()方法都是synchronized方法,进入synchronized方法体,意味着这个线程获取到了该“仓库”对象的同步锁。这也就是说,同一时间,生产者和消费者线程只能有一个能运行。通过同步锁,实现了对“残酷”的互斥访问。
对于生产方法produce()而言:当仓库满时,生产者线程等待,需要等待消费者消费产品之后,生产线程才能生产;生产者线程生产完产品之后,会通过notifyAll()唤醒同步锁上的所有线程,包括“消费者线程”,即我们所说的“通知消费者进行消费”。
对于消费方法consume()而言:当仓库为空时,消费者线程等待,需要等待生产者生产产品之后,消费者线程才能消费;消费者线程消费完产品之后,会通过notifyAll()唤醒同步锁上的所有线程,包括“生产者线程”,即我们所说的“通知生产者进行生产”。