java并发-线程
前言
近来时间比较充裕,正好又看了一遍《实战java高并发程序设计》,故而对java并发一些知识进行下总结,算是温故而知新吧。
一,线程基础
1,新建线程
一般有两种实现方式实现Runnable接口或继承Thread类(Thread类本身也是实现Runnable接口)
public class Test { public static void main(String[] args) throws Exception { Thread t1=new TestThread(); Thread t2=new TestThread(); Thread t3=new Thread(new TestThreadImpl()); Thread t4=new Thread(new TestThreadImpl()); t1.start(); t2.start(); t3.start(); t4.start(); } } class TestThread extends Thread{ @Override public void run() { System.out.println("hello word"); } } class TestThreadImpl implements Runnable{ @Override public void run() { System.out.println("hello word 2"); } }
2,线程的终止
一般来说线程在执行完毕后就会结束无须手动关闭,但凡是总有例外一下服务端的后台线程可能会常驻系统,比如它们本身就是一个无限循环,用于提供默写服务。
Thread 提供了一个被标注为废弃的方法stop();因为stop()方法过于暴力,强行把执行一般的线程终止,这样可能会引起一些数据不一致的问题。《实战java高并发程序设计》中给出了一个的解决方案,用一个一个自定义的stop并且需要自行决定何时退出
class ChangeObjectThread extends Thread{ volatile boolean stopme=false; public void stopMe(){ stopme=true; } @Override public void run() { while(true){ if(stopme){ break; } //synchromized handle data } } }
3,线程中断interrupt
严格来讲,线程中断并不会让线程立即退出,而是给线程发送一个通知,告知目标线程 ,有人希望你退出了。至于目标线程街道通知后如何处理,则完全有线程自行决定,如果无条件退出那么又遇到和stop()相同的问题了。
public void interrupt() //中断线程 public boolean isInterrupted()//判断是否被中断 public static boolean interrupted()//判断是否被中断,并清除当前中断状态
public class Test { public static Object o = new Object(); public static void main(String[] args) throws Exception { Thread t1 = new TestThread(); t1.start(); Thread.sleep(1000); t1.interrupt(); } } class TestThread extends Thread { @Override public void run() { int i=0; while (!isInterrupted()) { i++; System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i); } } }
另:当线程由于被调用了sleep(), wait(), join()等方法而进入阻塞状态;若此时调用线程的interrupt()将线程的中断标记设为true。由于处于阻塞状态,中断标记会被清除,同时产生一个InterruptedException异常,这时就要用合适的方式处理InterruptedException了
4,Object 等待(wait)和通知(notify)
这两个方法是由Object类提供的,并不是在Thread中的。当一个对象实例调用了wait()方法后,当前线程就会在这个对象上等待。如:线程A中调用了obj.wait()方法,那么线程A就会停止继续执行,而转为等待状态,直到调用了obj.notify()方法为止。
wait(),notify()方法的调用鼻血包含在对应的synchronized语句中,也就是要先获得目标对象锁资源。调用了wait后,会释放锁资源。
public class Test { public static Object o = new Object(); public static void main(String[] args) throws Exception { Thread t1 = new TestThread(); Thread t2 = new TestThread(); Thread t3 = new TestThread(); Thread t4 = new TestThreadNotify(); t1.start(); t2.start(); t3.start(); t4.start(); } } class TestThread extends Thread { @Override public void run() { synchronized (Test.o) { try { System.out.println(Thread.currentThread().getName()+"waiting...."); Test.o.wait(); System.out.println(Thread.currentThread().getName()+"start...."); Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"stop...."); } } } class TestThreadNotify extends Thread { @Override public void run() { synchronized (Test.o) { System.out.println("TestThreadNotify start...."); try { Test.o.notifyAll(); } catch (Exception e) { e.printStackTrace(); } System.out.println("TestThreadNotify stop...."); } } } }
Thread-0waiting.... TestThreadNotify start.... TestThreadNotify stop.... Thread-2waiting.... Thread-1waiting.... Thread-0start.... Thread-0stop....
5,等待线程结束(join)和谦让(yield)
很多时候,一个线程的输入可能非常依赖于另外一个或者多个线程的输出,此时,这个线程就需要等待依赖线程执行完毕,才能继续执行。JDK 提供了join()操作来实现这个功能,如下所示,显示了2个join()方法:
public final void join() throws InterruptedException public final synchronized void join(long millis) throws InterruptedException
第一个join()方法表示无限等待,它会一直阻塞当前线程,直到目标线程执行完毕。
第二个方法给出了一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程也会因为“等不及了”,而继续往下执行。
join()的本质是让调用线程wait()在当前线程对象实例上;
public class Test { public volatile static int i = 0; public static class AddThread extends Thread { @Override public void run() { for (i = 0; i < 10000000; i++); } } public static void main(String[] args) throws InterruptedException { AddThread at = new AddThread(); at.start(); at.join(); System.out.println(i); } }
主函数中,如果不使用join()等待AddThread ,那么得到的i 很可能是0或者一个非常小的数字。因为AddThread 还没开始执行,i 的值就已经被输出了。但在使用join()方法后,表示主线程愿意等待AddThread 执行完毕,跟着AddThread 一起往前走,故在join()返回时,AddThread 已经执行完成,故i 总是10000000
yiead()
这是一个静态方法,一旦执行,它会使当前线程让出CPU 。但要注意,让出CPU 并不表示当前线程不执行了。当前线程在让出CPU 后,还会进行CPU 资源的争夺,但是是否能够再次被分配到,就不一定了
待续