Thread类的基本使用(二) 线程的join(等待线程结束)和yield(谦让线程) 线程的join方法的意思是,如果有对象在该线程中执行,但是又有别的线程要调用这个对象,join方法会让别的线程阻塞,直到该线程执行完毕为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class test implements Runnable { public static int i; public static void main (String[] args) throws InterruptedException { test r = new test(); Thread t = new Thread(r); t.start(); t.join(); System.out.println("i=" +i); } @Override public void run () { for (i = 0 ; i<10000 ; i++) {} } }
这里可以看到的是,t线程一直在执行run方法,如果此时没有join方法的话,就直接执行了之后的print方法,这样打印出来的数值就是0因为join方法阻塞了主线程调用print方法去打印i。
再看看join的内部构造
1 2 3 public final void join () throws InterruptedException { join(0 ); }
这个内部表示可以向join传入一个参数,表示等待的时间,看看我们传入了100ms之后,会出现什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class test implements Runnable { public static int i; public static void main (String[] args) throws InterruptedException { test r = new test(); Thread t = new Thread(r); t.start(); t.join(100 ); System.out.println("i=" +i); } @Override public void run () { for (i = 0 ; i<1000000000 ; i++) {} } }
看,线程t在100ms后便不再阻塞主线程,任由其打印了。
那么再看看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public final synchronized void join (long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0 ; if (millis < 0 ) { throw new IllegalArgumentException("timeout value is negative" ); } if (millis == 0 ) { while (isAlive()) { wait(0 ); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0 ) { break ; } wait(delay); now = System.currentTimeMillis() - base; } } }
取重点
1 2 3 4 if (millis == 0 ) { while (isAlive()) { wait(0 ); }
这个while循环会一直判断,是否一直存活,若存活则一直继续,若不为存活,则直接跳出循环并使用wait(delay)方法,它会传入参数,在调用notifyAll()方法,唤醒全部线程。
线程谦让:
1 public static native void yield () ;
线程谦让表示该线程愿意让出CPU资源,给其他线程去调用,但这并不表示该线程就不执行了,它仅仅是表明了,“我愿意让出这个资源,大家再度公平竞争”,这通常会在某些运算中运用到,比如有一个线程调用了大量的cpu资源去给线程a运算一个一千万循环的for语句,和一百循环的for语句,这样相比cpu会给极大的资源给线程a运算第一个for,但当其运算完成后,第二个for想必不用调用这么多cpu资源去运算,那么便可以使用线程谦让,使得它们再次平衡下来。
线程的suspend(线程挂起)和resume(继续执行) 线程的suspend方法和resume方法早已被废弃,不过这里还是简单讲一下:
线程挂起的意思就是将当前的线程阻塞,不让其继续使用,等待线程收到resume命令之后才会继续执行
1 2 3 4 5 6 7 8 9 10 11 12 t.start(); t.suspend(); Thread.sleep(1000 ); i=1 ; t.resume(); public void run () { while (true ){ if (i==1 ){ System.out.println("ok!!!" ); break ; }
但是这样方法在某些情况的使用是致命的,因为被挂起的线程是不会释放任何资源的,如果线程此时被上了锁,那将会有非常多的线程去等待一个线程释放资源,从而导致长时间的阻塞,而且,在另一个线程去调用resume的同时,你也不能确定resume方法一定会在suspend之后执行,因为在并行计算中,这一切不一定完全有序,这样会导致死锁的产生,所以这两个方法被废弃了。
那有什么可以代替的呢?
线程的wait(等待)和notify(通知) 这里可以引入到两个新的方法wait和notify,wait方法和suspend方法一样,会让线程阻塞,然后等待通知了才会继续。
1 2 3 4 5 6 7 8 9 10 11 12 13 public final void wait (long timeout, int nanos) throws InterruptedException { if (timeout < 0 ) { throw new IllegalArgumentException("timeout value is negative" ); } if (nanos < 0 || nanos > 999999 ) { throw new IllegalArgumentException( "nanosecond timeout value out of range" ); } if (nanos > 0 ) { timeout++; } wait(timeout); }
但是注意,wait方法的使用是被锁包围的,首先wait在执行之前会被锁给锁住,不让其它线程获取本线程(线程a)的对象资源,而使用了wait后,他会自动且暂时的释放当前的锁,然后阻塞该线程,等待需要访问该对象的线程b获得对象锁,再使用notify方法后,执行完成线程b的synchronize代码块后,再唤醒线程a,继续执行线程a。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class test { final static Object object = new Object(); public static class test1 extends Thread { @Override public void run () { synchronized (object) { System.out.println("test1" ); try { System.out.println("test1 wait" ); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test1 end" ); } } }public static class test2 extends Thread { @Override public void run () { synchronized (object) { System.out.println("test2" ); object.notify(); System.out.println("test2 end" ); } } } public static void main (String[] args) { Thread t1=new test1(); Thread t2=new test2(); t1.start(); t2.start(); } } test1 test1 wait test2 test2 end test1 end
区别的核心在于: 1、suspend()在引起当前所在线程阻塞后,不会释放线程占用的锁(如果占用了的话) 2、wait() 引起当前所在线程阻塞后,会释放占用的锁,并且必须 wait对象的锁必须被当前线程持有 3、wait与notify 需要 synchronized 锁来包裹
4、suspend会产生死锁问题,而wait不会
线程的stop(终止)和interrupted(中断) 线程可以在执行过程中被强行结束吗?当然可以,但是,强行被结束是会出现很多问题的,最初使用的方法叫stop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Deprecated public final void public final void stop () { SecurityManager security = System.getSecurityManager(); if (security != null ) { checkAccess(); if (this != Thread.currentThread()) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } if (threadStatus != 0 ) { resume(); } stop0(new ThreadDeath()); }
想必大家也很容易的猜得到,stop方法被弃用的原因是会破坏对象原子性,即使在被synchronize包围时也是如此,因为stop方法是先去释放锁,再去停止整个线程的,这样在释放锁的过程中,可能会出现一释放锁,该锁立马被别的线程锁获取,并发生了修改对象操作后,便停止了线程,这种数据错误不容易在众多代码出找出来,可以说是非常致命,那么对应的又有什么方式去代替它呢,当然有,那就是interrupt()方法,线程中断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void interrupt () { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null ) { interrupt0(); b.interrupt(this ); return ; } } interrupt0(); }
那么这个interrupt要如何使用呢,直接像stop一样使用吗? 并不对,这样其实并不会停止线程。interrupt仅仅是产生了一个中断标记,表示该线程即将要中断了,至于什么时候中断,则是由设计者决定的了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class test implements Runnable { public static void main (String[] args) throws InterruptedException { Thread t = new Thread(new test()); t.start(); t.interrupt(); } @Override public void run () { while (true ) { if (Thread.currentThread().isInterrupted()) { break ; } } } }
Thread.interrupt() //中断线程 Thread.isInterrupted()判断是否被中断 Thread.interrupted()判断是否被中断,并清除当前中断状态