线程控制基本方法
方 法 功 能
isAlive() 判断线程是否还“活”着,即当前run线程是否还未终止。
getPriority() 获得线程的优先级数值
setPriority() 设置线程的优先级数值
Thread.sleep() 将当前线程睡眠指定毫秒数
join() 调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行。
yield()
让出CPU,当前线程进入就绪队列等待调度。
wait()
当前线程进入对象的wait pool。
notify()/notifyAll() 唤醒对象的wait
pool中的一个/所有等待线程。
run()和start()
这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用
run()方法,这是由Java的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。
isAlive方法实例:
1 package com.Gavin.createthread; 2 3 public class TestIsAlive { 4 5 public static void main(String[] args) { 6 Thread6 t6 = new Thread6("t6"); 7 t6.start(); 8 9 for(int i = 0; i < 50; i++) { 10 // System.out.println(Thread.currentThread().getName()); //主线程 11 System.out.println("t6‘s name:" + t6.getName()); 12 } 13 } 14 15 } 16 17 class Thread6 extends Thread { 18 public Thread6(String string) { 19 super(string); 20 } 21 public void run() { 22 System.out.println("thread is alive:" + Thread.currentThread().isAlive()); 23 24 for(int i = 0; i < 50; i++) { 25 System.out.println("subThread:" + i); 26 } 27 } 28 }
interrupt/sleep方法:
可以调用Thread的静态方法: public static void
sleep(long millis) throws InterruptedException
使得当前线程休眠(暂时停止执行millis毫秒)。
由于是静态方法,sleep可以由类名直接调用:Thread.sleep(…)
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是如果有Synchronized同步块,其他线程仍然不同访问共享数据。注意该方法要捕获异常
比如有两个线程同时执行(没有Synchronized),一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没 有 Sleep()方法,只有高优先级的线程执行完成后,低优先级的线程才能执行;但当高优先级的线程sleep(5000)后,低优先级就有机会执行了。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
1 package com.Gavin.createthread; 2 3 import java.util.Date; 4 5 public class TestInterrupt { 6 public static void main(String[] args) { 7 Thread1 t = new Thread1(); 8 t.start(); 9 /** 10 * api中称:中断线程 11 * Thread1线程与主线程开始并发,主线程让出10s, 12 * 然后调用interrupt强行终止Thread1线程 13 */ 14 try { 15 Thread.sleep(10000); 16 } catch(InterruptedException e) { 17 } 18 19 t.interrupt(); 20 } 21 } 22 23 class Thread1 extends Thread { 24 public void run() { 25 while (true) { 26 System.out.println("===" + new Date()+ "==="); 27 try { 28 sleep(1000); 29 } catch (InterruptedException e) { 30 return; 31 } 32 } 33 } 34 }
停止线程的方法中,stop最强暴,其次便是interrupte,这两种都是不提倡的,推荐的方法是通过flag标志来终止线程,例如:
1 package com.Gavin.createthread; 2 public class TestShutDown { 3 public static void main(String[] args) { 4 Thread5 t5 = new Thread5(); 5 Thread t = new Thread(t5); 6 t.start(); 7 for(int i = 0; i < 100; i++) { 8 System.out.println("main thread i:" + i); 9 } 10 System.out.println("main thread is over"); 11 /** 12 * 通过操作flag标志来关闭线程,推荐使用 13 */ 14 t5.shutDown(); 15 // t.stop(); //太暴力,不推荐 16 } 17 } 18 class Thread5 implements Runnable { 19 private boolean flag = true; 20 public void run() { 21 int i = 0; 22 while(flag) { 23 System.out.println("value:" + i++); 24 } 25 } 26 public void shutDown() { 27 flag = false; 28 } 29 }
join方法:
合并某个线程,,join()方法使调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行。注意该方法也要捕获异常。
1 package com.Gavin.createthread; 2 3 public class TestJoin { 4 public static void main(String[] args) { 5 //指定新的线程对象,并通过super指向Thread,指定线程名称 6 Thread2 t = new Thread2("Thread2 "); 7 t.start(); 8 /** 9 * api中称:等待该线程终止。 10 * 当线程启动后,原本线程是会与主线程并发执行 11 * 当调用了join方法后,意味着Thread2线程将会与主线程合并 12 * 所以,需要等待Thread2线程执行完毕,合并后,主线程才会继续执行 13 */ 14 try { 15 t.join(); 16 } catch (InterruptedException e) { 17 } 18 for(int i = 0; i <= 10; i++) { 19 System.out.println("i am main thread " + i); 20 } 21 } 22 } 23 24 class Thread2 extends Thread { 25 Thread2(String string) { 26 super(string); 27 } 28 public void run() { 29 for(int i = 0; i <= 10; i++) { 30 System.out.println("i am " + getName() + i); 31 try { 32 /** 33 * api中称:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行), 34 * 此操作受到系统计时器和调度程序精度和准确性的影响 35 */ 36 sleep(1000); 37 } catch (InterruptedException e) { 38 return; 39 } 40 } 41 } 42 }
yield方法:
暂时让出CPU,给其他线程执行的机会,它与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
1 package com.Gavin.createthread; 2 3 public class TestYield { 4 public static void main(String[] args) { 5 Thread3 t = new Thread3("t"); 6 Thread3 tt = new Thread3("tt"); 7 t.start(); 8 tt.start(); 9 } 10 } 11 class Thread3 extends Thread { 12 Thread3(String string) { 13 super(string); 14 } 15 public void run () { 16 for(int i = 0; i < 100; i++) { 17 System.out.println(getName() + ":" + i); 18 if(i % 10 == 0) { 19 /** 20 * api中称:暂停当前正在执行的线程对象,并执行其他线程。 21 * 注:暂停时间片不定,只是到条件即暂时让出cpu 22 */ 23 yield(); 24 } 25 } 26 } 27 }
setPriority():
1 package com.Gavin.createthread; 2 3 public class TestPriority { 4 5 public static void main(String[] args) { 6 Thread t1 = new Thread(new T1()); 7 Thread t2 = new Thread(new T2()); 8 /** 9 * 未设置优先级的,t1,t2将几乎拥有均等的时间片,交互执行 10 * 但是当设置优先级后,t1会拥有更长时间片。甚至t1在抢夺的时间片内已执行完。 11 */ 12 t1.setPriority(Thread.NORM_PRIORITY + 3); 13 14 t1.start(); 15 t2.start(); 16 } 17 18 } 19 20 class T1 implements Runnable { 21 public void run() { 22 for(int i = 0; i < 200; i++) { 23 System.out.println("T1:" + i); 24 } 25 } 26 } 27 28 class T2 implements Runnable { 29 public void run() { 30 for(int i = 0; i < 200; i++) { 31 System.out.println("T2:" + i); 32 } 33 } 34 35 }
关键字Synchronized 这个关键字用于保护共享数据,当然前提是要分清哪些数据是共享数据。每个对象都有一个锁标志,当一个线程访问该对象时,被Synchronized修饰的数据将被“上锁”,阻止其他线程访问。当前线程访问完这部分数据后释放锁标志,其他线程就可以访问了。
1 //同步锁 2 public class TestSync implements Runnable { 3 Timer timer = new Timer(); 4 public static void main(String[] args) { 5 TestSync test = new TestSync(); 6 Thread t1 = new Thread(test); 7 Thread t2 = new Thread(test); 8 9 //t1.setName("t1"); 10 //t2.setName("t2"); 11 t1.start(); 12 t2.start(); 13 } 14 public void run() { 15 timer.add(Thread.currentThread().getName()); 16 } 17 } 18 class Timer{ 19 private static int num = 0; 20 21 //synchronized 锁定当前对象 22 public synchronized void add(String name) { 23 //synchronized(this) { 24 num++; 25 try { 26 Thread.sleep(1); 27 } catch (InterruptedException e){ 28 29 } 30 System.out.println(name + ",你是第" + num + "个使用timer的线程"); 31 //} 32 } 33 }
注意以下这个例子
1 public ThreadTest implements Runnable { 2 public synchronized void run(){ 3 for(int i=0;i<10;i++) { 4 System.out.println(" " + i); 5 } 6 } 7 public static void main(String[] args) { 8 Runnable r1 = new ThreadTest(); 9 Runnable r2 = new ThreadTest(); 10 Thread t1 = new Thread(r1); 11 Thread t2 = new Thread(r2); 12 t1.start(); 13 t2.start(); 14 } 15 }
结果: 0 0 1 2 3 4 1 5 2 6 3 7 4 8 9 5 6 7 8 9 ;(不同对象)
以上这段程序中的 i
变量并不是共享数据,这个程序中的t1,t2分别是两个对象(r1,r2)的线程。JAVA是面向对象的程序设计语言,不同的对象的数据是不同
的,r1,r2有各自的run()方法,而synchronized使同一个对象的多个线程,在某个时刻只有其中的一个线程可以访问这个对象的
synchronized数据。
当把代码改成如下:Synchronized关键字才会起作用
Runnable r = new ThreadTest();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
synchronized 导致的死锁问题:
1 //死锁典型:哲学家吃饭问题 2 public class TestDeadLock implements Runnable{ 3 public int flag = 1; 4 static Object o1 = new Object(); 5 static Object o2 = new Object(); 6 public void run() { 7 System.out.println("flag=" + flag); 8 if(flag == 1) { 9 synchronized(o1) { 10 try { 11 Thread.sleep(500); 12 }catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 synchronized(o2) { 16 System.out.println("1"); 17 } 18 } 19 } 20 21 if(flag == 0) { 22 synchronized(o2) { 23 try { 24 Thread.sleep(500); 25 }catch (InterruptedException e) { 26 e.printStackTrace(); 27 } 28 synchronized(o1) { 29 System.out.println("0"); 30 } 31 32 } 33 } 34 } 35 36 public static void main(String[] args) { 37 TestDeadLock td1 = new TestDeadLock(); 38 TestDeadLock td2 = new TestDeadLock(); 39 td1.flag = 1; 40 td2.flag = 0; 41 Thread t1 = new Thread(td1); 42 Thread t2 = new Thread(td2); 43 t1.start(); 44 t2.start(); 45 } 46 }
wait()和notify()、notifyAll()
这三个方法用于协调多个线程对共享数据的存取,所以必须在Synchronized语句块内使用这三个方法。前面说过Synchronized这个 关键字用于保护共享数据,阻止其他线程对共享数据的存取。但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出Synchronized数据块时 让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。
wait()方法使当前线程暂停执行并释放对象锁标志,让其他线程可以进入Synchronized数据块,当前线程被放入对象等待池中。当调用 notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有
锁标志等待池中的线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
注意 这三个方法都是java.lang.Ojbect的方法!