线程的两种实现方式,以及区别
- 让类成为线程类有两种方式,实现Runnable接口,以及继承Thread类(类中实现了Runnable接口,还提供了一些额外的方法)。
一、Runnable相对优势:
-
java的单继承,当继承了Thread类,则不能继承其他类,而实现Runnable接口可以
-
实现Runnable接口的线程类的多个线程,可以访问同一变量,而Thread则不能(多窗口买票问题)
原因:两种方式启动方式不同:
- Runnable线程类是实例化一个对象o之后,通过多次new Thread(o).start();启动多个线程,而这几个线程属于一个对象,对象的成员变量是同一个。
- Thread线程类启动多个线程需要 new MyThread().start();每个线程启动都对应多个对象,他们的成员变量是独立的。
测试代码如下:
(1)实现Runnable:
1 package com.loan.entity; 2 3 import lombok.Data; 4 5 @Data 6 public class Test2 implements Runnable{ 7 private int ticket=100; 8 @Override 9 public void run() { 10 // TODO Auto-generated method stub 11 while(true){ 12 if(ticket>0){ 13 System.out.println(Thread.currentThread().getName()+"...is saling,余票:"+ticket--); 14 } 15 } 16 } 17 public static void main(String[] args) { 18 Test2 t=new Test2();//只能使用同一个t 19 new Thread(t).start(); 20 new Thread(t).start(); 21 new Thread(t).start(); 22 new Thread(t).start(); 23 } 24 }
运行结果:
(2)继承Thread
1 package com.loan.entity; 2 3 public class Test3 extends Thread{ 4 int ticket=100; 5 public void run(){ 6 while(true){ 7 if(ticket>0){ 8 System.out.println(Thread.currentThread().getName()+"...is saling,余票:"+ticket--); 9 } 10 } 11 } 12 public static void main(String[] args) { 13 new Test3().start(); 14 new Test3().start(); 15 new Test3().start(); 16 new Test3().start(); 17 } 18 }
很明显,上面这种方式是错误的!
继承Thread类也可以通过内部类发方式实现。代码如下:
1 package com.loan.entity; 2 3 public class Test3{ 4 private int ticket=100; 5 class InnerClass extends Thread{ 6 private Test3 t3; 7 InnerClass(Test3 t){ 8 t3=t; 9 } 10 public void run(){ 11 while(true){ 12 if(ticket>0){ 13 ticket--; 14 System.out.println(Thread.currentThread().getName()+"...is saling,余票:"+ticket); 15 } 16 } 17 } 18 } 19 public static void main(String[] args) { 20 Test3 test3=new Test3(); 21 Thread t1=test3.new InnerClass(test3); 22 Thread t2=test3.new InnerClass(test3); 23 Thread t3=test3.new InnerClass(test3); 24 Thread t4=test3.new InnerClass(test3); 25 t1.setName("t1"); 26 t2.setName("t2"); 27 t3.setName("t3"); 28 t4.setName("t4"); 29 t1.start(); 30 t2.start(); 31 t3.start(); 32 t4.start(); 33 } 34 }
运行结果:
二、Thread优势
1、使用线程的方法方便一些,例如:获取线程的Id(Thread.currentThread().getId())、线程名(Thread.currentThread().getName())、线程状态(Thread.currentThread().getState())等
2、操作同一变量,但是线程调用run方法内容不同时,使用Thread内部类的方式进行,例如生产者、消费者模式
生产者消费者多线程例子:
1 package com.loan.entity; 2 3 public class Store { 4 private final int MAX_SIZE=2;//仓库总共可存放货物 5 private int count=0;//当前仓库货物 6 public synchronized void add() throws InterruptedException{ 7 while(count>=MAX_SIZE){ 8 System.out.println("仓库已满"); 9 System.out.println(Thread.currentThread().getName()+"等待中。。。。"); 10 this.wait(); 11 } 12 count++; 13 System.out.println(Thread.currentThread().getName()+"存入仓库,当前货物数:"+count); 14 this.notify(); 15 } 16 public synchronized void remove() throws InterruptedException{ 17 while(count<=0){ 18 System.out.println("仓库空了"); 19 System.out.println(Thread.currentThread().getName()+"等待中。。。。"); 20 this.wait(); 21 } 22 count--; 23 System.out.println(Thread.currentThread().getName()+"取出货物,当前货物数:"+count); 24 this.notify(); 25 } 26 public static void main(String[] args) { 27 Store s=new Store(); 28 Thread producer1=s.new Producer(s);//成员内部类需通过对象访问 29 Thread producer2=s.new Producer(s); 30 Thread consumer1=s.new Consumer(s); 31 Thread consumer2=s.new Consumer(s); 32 producer1.setName("producer1");//利用Thread中的方法 33 producer2.setName("producer2"); 34 consumer1.setName("consumer1"); 35 consumer2.setName("consumer2"); 36 producer1.start(); 37 producer2.start(); 38 consumer1.start(); 39 consumer2.start(); 40 } 41 class Producer extends Thread{ 42 private Store store; 43 Producer(Store s){ 44 store=s; 45 } 46 public void run(){ 47 while(true){ 48 try { 49 store.add(); 50 Thread.sleep(10000); 51 } catch (InterruptedException e) { 52 // TODO Auto-generated catch block 53 e.printStackTrace(); 54 } 55 } 56 } 57 } 58 class Consumer extends Thread{ 59 private Store store; 60 Consumer(Store s){ 61 store=s; 62 } 63 public void run(){ 64 while(true){ 65 try { 66 store.remove(); 67 Thread.sleep(15000); 68 } catch (InterruptedException e) { 69 // TODO Auto-generated catch block 70 e.printStackTrace(); 71 } 72 } 73 } 74 } 75 }