定义线程的两种方式
1:继承Thread
2:实现Runnable接口
创建线程对象也有两种方法
1:继承Thread类定义线程
采用这种方式最重要的一点就是需要重写Thread中的run方法。
Example
Public class XianCheng extends Thead { public void run(){ System.out.println("通过继承Thread定义线程"); } }
重写的run方法可以作为一般方法调用,run方法也可以被重载,但这两种运行的,都不作为一个线程,而是作为主线程的一部分。
创建线程对象:
XianCheng = new Xiancheng();
2:实现Runnable接口定义线程
public class XianCheng2 implements Runnable { public void run(){ System.out.println("通过实现Runnable接口定义线程"); } }
用这种方法创建线程,必须借助Thread类。
创建对象:
XianCheng2 xc = new XianCheng(); Thread t = new Thread(xc);
运行线程
线程创建之后只是普通的对象,要想让它作为线程启动,必须进行启动。
Example:
1 class MyRunnable implements Runnable 2 { 3 public void run(){ 4 System.out.println("正确启动线程"); 5 } 6 } 7 public class XianCheng4 8 { 9 public static void main(String args[]){ 10 MyRunable mr = new MyRunnable(); 11 Thread t = new Thread(mr); 12 t.start(); 13 } 14 }
同时运行多个线程
class MyRunnable implements Runnable{ public void run(){ for(int i=0;i<10;i++){ System.out.println("@"); } } } class MyRunnable2 implements Runnable{ public void run(){ for(int i=0;i<10;i++){ System.out.println("#"); } } } public class XianCheng6{ public static void main(String arg[]){ MyRunnable mr1 = new MyRunnable(); MyRunnable2 mr2 = new MyRunnable2(); Thread t1 = new Thread(mr1); Thread t2 = new Thread(mr2); t1.start(); t2.start(); } }
线程调度的方法
线程调度有以下几种方法:睡眠方法,设置线程优先级,Yield让步方法,Join让步方法
睡眠方法: 并不是线程调用sleep方法,而是线程执行sleep方法。
class MyRunnable implements Runnable{ public void run(){ for(int i=0;i<100;i++){ System.out.print("@"); try{ Thread.sleep(50); }catch(InterruptedException e){ e.printStackTrace(); } } } } class MyRunnable2 implements Runnable{ public void run(){ for(int i=0;i<100;i++){ System.out.print("#"); try{ Thread.sleep(50); }catch(InterruptedException e){ e.printStackTrace(); } } } } public class thread{ public static void main(String arg[]){ MyRunnable mr1 = new MyRunnable(); MyRunnable2 mr2 = new MyRunnable2(); Thread t1 = new Thread(mr1); Thread t2 = new Thread(mr2); t1.start(); t2.start(); } }
设置优先级:默认优先级是5,java中优先级1-10.调用setPriority(int i)可以设置优先级
public final void setPriority(int i);
Example:
class MyRunnable implements Runnable{ public void run(){ for(int i=0;i<100;i++){ System.out.print("@"); } } } class MyRunnable2 implements Runnable{ public void run(){ for(int i=0;i<100;i++){ System.out.print("#"); } } } public class thread{ public static void main(String arg[]){ MyRunnable mr1 = new MyRunnable(); MyRunnable2 mr2 = new MyRunnable2(); Thread t1 = new Thread(mr1); Thread t2 = new Thread(mr2); t1.setPriority(1); t2.setPriority(10); t1.start(); t2.start(); } }
yield让步方法:yield可以让当前运行的线程让出当前cpu,使线程回到准备状态,让其他线程进入运行状态,而将cpu给哪个线程并不确定,由系统决定。
需要线程执行yield方法。
public static void yield();
class MyRunnable implements Runnable{ public void run(){ for(int i=0;i<100;i++){ System.out.print("@"); Thread.yield(); } } } class MyRunnable2 implements Runnable{ public void run(){ for(int i=0;i<100;i++){ System.out.print("#"); Thread.yield(); } } } public class thread{ public static void main(String arg[]){ MyRunnable mr1 = new MyRunnable(); MyRunnable2 mr2 = new MyRunnable2(); Thread t1 = new Thread(mr1); Thread t2 = new Thread(mr2); t1.start(); t2.start(); } }
join让步方法:使用join可以将cpu让步给指定线程。当一个线程必须在另一个线程执行完成后才能运行的时候可以使用join让步方法。
public final void join() throws InterruptedException;
public final void join(long mills)throws InterruptedException;
public final void join(long mills,int nanos) throws InterruptedException;
class MyRunnable implements Runnable{ public void run(){ for(int i=0;i<20;i++){ System.out.print("@"); } } } public class thread{ public static void main(String arg[]){ MyRunnable mr1 = new MyRunnable(); Thread t1 = new Thread(mr1); t1.start(); for(int i=0;i<100;i++){ if(i==10){ try{ t1.join(); }catch(InterruptedException e){ e.printStackTrace(); } } System.out.print("#"); } } }
wait,notify,notifyall
是Object 类的方法。所有对象都拥有这一对方法。这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。
notify()是释放对象的wait()方法而阻塞线程(但是也要当得到锁后才可以运行)但是这个释放是随机的,也就是不一定要释放那个线程。(因为调用同一资源的可能不是一个线程或者说是有多个阻塞的线程在等待,但是如果加了synchronized也只有一个线程,也有其他的线程在等待中,也就是阻塞)我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。
除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。
同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现 IllegalMonitorStateException 异常。
举例:生产者与消费者
1 public class ProducerConsumer { 2 public static void main(String args[]){ 3 SyncStack ss = new SyncStack(); 4 Producer p = new Producer(ss); 5 Consumer c = new Consumer(ss); 6 new Thread(p).start(); 7 new Thread(c).start(); 8 } 9 } 10 class WoTou{ 11 int id ; 12 public WoTou(int id){ 13 this.id = id; 14 } 15 public String toString(){ 16 return "WoTou:"+id; 17 } 18 } 19 class SyncStack{ 20 int index = 0; 21 WoTou[] awtWT = new WoTou[6]; 22 23 public synchronized void push(WoTou wt){ 24 while(index == awtWT.length){ 25 try { 26 this.wait();//当前的正在访问对象的线程wait 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 } 31 this.notify(); 32 awtWT[index] = wt; 33 index ++; 34 } 35 36 public synchronized WoTou pop(){ 37 while(index == 0){//if->while:如果catch住异常后,假如是if并且index=0,index == 0 不再判断,index--会执行, 程序错误。所以改为while,可以避免发生如此错误。 38 try { 39 this.wait();//当前的正在访问对象的线程阻塞(wait),wait后对象的锁不归线程所有,直到"醒了"(notify,notifyALL),再重新找回锁。 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 } 44 this.notify();//叫醒一个正在当前对象上wait的线程。 45 index--; 46 return awtWT[index]; 47 } 48 } 49 50 class Producer implements Runnable{ 51 SyncStack ss = null; 52 Producer(SyncStack ss){ 53 this.ss = ss; 54 } 55 public void run() { 56 for(int i=0;i<20;i++){ 57 WoTou wt = new WoTou(i); 58 ss.push(wt); 59 System.out.println("Producer:"+wt); 60 try { 61 Thread.sleep((int)(Math.random()*100)); 62 } catch (InterruptedException e) { 63 e.printStackTrace(); 64 } 65 } 66 } 67 } 68 class Consumer implements Runnable{ 69 SyncStack ss = null; 70 Consumer(SyncStack ss){ 71 this.ss = ss; 72 } 73 public void run() { 74 for(int i=0;i<20;i++){ 75 WoTou wt = ss.pop(); 76 System.out.println("Consumer:"+wt); 77 try { 78 Thread.sleep((int)(Math.random()*1000));//目的是让其他线程有执行的机会。 79 } catch (InterruptedException e) { 80 e.printStackTrace(); 81 } 82 } 83 } 84 } 85 执行结果: 86 87 Producer:WoTou:0 88 Consumer:WoTou:0 89 Producer:WoTou:1 90 Producer:WoTou:2 91 Producer:WoTou:3 92 Producer:WoTou:4 93 Producer:WoTou:5 94 Producer:WoTou:6 95 Consumer:WoTou:6 96 Producer:WoTou:7 97 Producer:WoTou:8 98 Consumer:WoTou:7 99 Producer:WoTou:9 100 Consumer:WoTou:8 101 Consumer:WoTou:9 102 Producer:WoTou:10 103 Consumer:WoTou:10 104 Producer:WoTou:11 105 Consumer:WoTou:11 106 Producer:WoTou:12 107 Consumer:WoTou:12 108 Producer:WoTou:13 109 Consumer:WoTou:13 110 Producer:WoTou:14 111 Consumer:WoTou:14 112 Producer:WoTou:15 113 Consumer:WoTou:15 114 Producer:WoTou:16 115 Consumer:WoTou:16 116 Producer:WoTou:17 117 Consumer:WoTou:17 118 Producer:WoTou:18 119 Consumer:WoTou:18 120 Producer:WoTou:19 121 Consumer:WoTou:19 122 Consumer:WoTou:5 123 Consumer:WoTou:4 124 Consumer:WoTou:3 125 Consumer:WoTou:2 126 Consumer:WoTou:1