让类成为线程类有两种方式,实现Runnable接口,以及继承Thread类(类中实现了Runnable接口,还提供了一些额外的方法)。
一、Runnable相对优势:
1、java的单继承,当继承了Thread类,则不能继承其他类,而实现Runnable接口可以
2、实现Runnable接口的线程类的多个线程,可以访问同一变量,而Thread则不能(多窗口买票问题)
原因:两种方式启动方式不同:
Runnable线程类是实例化一个对象o之后,通过多次new Thread(o).start();启动多个线程,而这几个线程属于一个对象,对象的成员变量是同一个。
Thread线程类启动多个线程需要 new MyThread().start();每个线程启动都对应多个对象,他们的成员变量是独立的。
测试代码如下:
(1)实现Runnable:
package com.loan.entity;
import lombok.Data;
@Data
public class Test2 implements Runnable{
private int ticket=100;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"...is saling,余票:"+ticket--);
}
}
}
public static void main(String[] args) {
Test2 t=new Test2();//只能使用同一个t
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
运行结果:
(2)继承Thread
package com.loan.entity;
public class Test3 extends Thread{
int ticket=100;
public void run(){
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"...is saling,余票:"+ticket--);
}
}
}
public static void main(String[] args) {
new Test3().start();
new Test3().start();
new Test3().start();
new Test3().start();
}
}
运行结果:
很明显,这种方式是错误的!
继承Thread类也可以通过内部类发方式实现。代码如下:
package com.loan.entity;
public class Test3{
private int ticket=100;
class InnerClass extends Thread{
private Test3 t3;
InnerClass(Test3 t){
t3=t;
}
public void run(){
while(true){
if(ticket>0){
ticket--;
System.out.println(Thread.currentThread().getName()+"...is saling,余票:"+ticket);
}
}
}
}
public static void main(String[] args) {
Test3 test3=new Test3();
Thread t1=test3.new InnerClass(test3);
Thread t2=test3.new InnerClass(test3);
Thread t3=test3.new InnerClass(test3);
Thread t4=test3.new InnerClass(test3);
t1.setName("t1");
t2.setName("t2");
t3.setName("t3");
t4.setName("t4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果:
二、Thread优势
1、使用线程的方法方便一些,例如:获取线程的Id(Thread.currentThread().getId())、线程名(Thread.currentThread().getName())、线程状态(Thread.currentThread().getState())等
2、操作同一变量,但是线程调用run方法内容不同时,使用Thread内部类的方式进行,例如生产者、消费者模式
生产者消费者多线程例子:
package com.loan.entity;
public class Store {
private final int MAX_SIZE=2;//仓库总共可存放货物
private int count=0;//当前仓库货物
public synchronized void add() throws InterruptedException{
while(count>=MAX_SIZE){
System.out.println("仓库已满");
System.out.println(Thread.currentThread().getName()+"等待中。。。。");
this.wait();
}
count++;
System.out.println(Thread.currentThread().getName()+"存入仓库,当前货物数:"+count);
this.notify();
}
public synchronized void remove() throws InterruptedException{
while(count<=0){
System.out.println("仓库空了");
System.out.println(Thread.currentThread().getName()+"等待中。。。。");
this.wait();
}
count--;
System.out.println(Thread.currentThread().getName()+"取出货物,当前货物数:"+count);
this.notify();
}
public static void main(String[] args) {
Store s=new Store();
Thread producer1=s.new Producer(s);//成员内部类需通过对象访问
Thread producer2=s.new Producer(s);
Thread consumer1=s.new Consumer(s);
Thread consumer2=s.new Consumer(s);
producer1.setName("producer1");//利用Thread中的方法
producer2.setName("producer2");
consumer1.setName("consumer1");
consumer2.setName("consumer2");
producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
}
class Producer extends Thread{
private Store store;
Producer(Store s){
store=s;
}
public void run(){
while(true){
try {
store.add();
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class Consumer extends Thread{
private Store store;
Consumer(Store s){
store=s;
}
public void run(){
while(true){
try {
store.remove();
Thread.sleep(15000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
运行结果: