Java基础及提高 之 线程创建的两种方式Thread VS Runnable(附卖票和取钱问题)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013987258/article/details/82689100

1.首先来说说创建线程的两种方式

一种方式是继承Thread类,并重写run()方法

public class MyThread extends Thread{
     @Override
     public void run() {
         // TODO Auto-generated method stub
         
     }
 }
 //线程使用
 MyThread mt = new MyThread();  //创建线程
 mt.start();                   //启动线程

实例:

public class ThreadDemo {

	public static void main(String[] args) {

		MyThread myThread1 = new MyThread("线程一"); // 第一种设置线程名称方法
		myThread1.start();	
		MyThread myThread2 = new MyThread();
		myThread2.setName("线程二"); // 第二种设置线程名称方法
		myThread2.start();
	}
}

class MyThread extends Thread{
	
	public MyThread(String name) {
		super(name);
	}
	public MyThread() {
	}
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println(this.getName() + " " + i);
		}
	}
}

运行结果为:

线程一 0
线程二 0
线程一 1
线程二 1
线程一 2
线程二 2
线程一 3
线程二 3
线程一 4
线程二 4

注意:每次运行结果顺序都不一样

另外一种方式是实现Runnable接口

public class MyThread implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        
    }
}
//线程使用
MyThread mt = new MyThread();
Thread thread = new Thread(mt); //创建线程
thread.start();                 //启动线程

实例:

public class RunnableDemo {

	public static void main(String[] args) {

		MyRunnable myRunnable = new MyRunnable();
		Thread thread1 = new Thread(myRunnable,"线程一");
		thread1.start();
		
		Thread thread2 = new Thread(myRunnable,"线程二");
		thread2.start();
	}

}

class MyRunnable implements Runnable{

	@Override
	public void run() {
		
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName() + " " + i);
		}
		
	}
	
}

运行结果为:

线程二 0
线程一 0
线程二 1
线程一 1
线程二 2
线程一 2
线程二 3
线程一 3
线程二 4
线程一 4

注意:这两种方法每次运行都是不一样的

2.两种方式创建线程比较

第一点:通过创建线程方式可以看出,一个是继承一个是实现接口,但是Java是只能继承一个父类,可以实现多个接口的一个特性,所以说采用Runnable方式可以避免Thread方式由于Java单继承带来的缺陷。

第二点:Runnable的代码可以被多个线程共享(Thread实例),适合于多个多个线程处理统一资源的情况。

  举例说明:模拟卖票,假设还剩10张票,分别采用两种方式来创建线程模拟

1.先用继承Thread的方法

public class ThreadTicketDemo {

	public static void main(String[] args) {

		ThreadTicket thread1 = new ThreadTicket("窗口一");
		thread1.start();
		
		ThreadTicket thread2 = new ThreadTicket("窗口二");
		thread2.start();
	}

}
class ThreadTicket extends Thread{
	
	private int  num = 10;
	public ThreadTicket(String name) {
		super(name);
	}
	@Override
	public void run() {
		while (true) {
			num --;
			if (num == 0) {
				System.out.println(this.getName() + "票已卖完");
				break;
			} else {
				System.out.println(this.getName() + "卖出一张票,剩余" + num + "张票");
			}
		}
	}
}

结果为:

窗口一卖出一张票,剩余9张票
窗口二卖出一张票,剩余9张票
窗口一卖出一张票,剩余8张票
窗口二卖出一张票,剩余8张票
窗口一卖出一张票,剩余7张票
窗口二卖出一张票,剩余7张票
窗口一卖出一张票,剩余6张票
窗口二卖出一张票,剩余6张票
窗口一卖出一张票,剩余5张票
窗口二卖出一张票,剩余5张票
窗口二卖出一张票,剩余4张票
窗口二卖出一张票,剩余3张票
窗口二卖出一张票,剩余2张票
窗口二卖出一张票,剩余1张票
窗口二票已卖完
窗口一卖出一张票,剩余4张票
窗口一卖出一张票,剩余3张票
窗口一卖出一张票,剩余2张票
窗口一卖出一张票,剩余1张票
窗口一票已卖完

 可以看出共卖出了20张票,变成了每个线程都有10张票了

2.再用实现了Runnable接口的方法

public class RunnableTicketDemo {

	public static void main(String[] args) {

		RunnableTicket runnable = new RunnableTicket();
		Thread thread1 = new Thread(runnable,"窗口一");
		thread1.start();

		Thread thread2 = new Thread(runnable,"窗口二");
		thread2.start();
	}

}

class RunnableTicket implements Runnable{
	
	private int num = 10;
	
	@Override
	public void run() {
		
		while (true) {
			synchronized(this) {
				try {
					Thread.sleep(100);  //更好的体现结果
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if (num > 0) {
					num --;
					if (num == 0) {
						System.out.println("票已卖完");
					} else {
						System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余" + num + "张票");
					}  
				}
			}
		}
	}
}

运行结果为:

窗口二卖出一张票,剩余9张票
窗口一卖出一张票,剩余8张票
窗口一卖出一张票,剩余7张票
窗口一卖出一张票,剩余6张票
窗口二卖出一张票,剩余5张票
窗口一卖出一张票,剩余4张票
窗口二卖出一张票,剩余3张票
窗口二卖出一张票,剩余2张票
窗口一卖出一张票,剩余1张票
票已卖完

在这个代码中我加入了synchronized(this) {}这一行代码,这个可以防止多个线程对一个数据的操作,如果不加会出现如下结果:

窗口二卖出一张票,剩余8张票
窗口二卖出一张票,剩余7张票
窗口一卖出一张票,剩余8张票
窗口二卖出一张票,剩余6张票
窗口一卖出一张票,剩余5张票
窗口二卖出一张票,剩余4张票
窗口一卖出一张票,剩余3张票
窗口二卖出一张票,剩余2张票
票已卖完
窗口一卖出一张票,剩余1张票

注意synchronized放置在while循环内部而不是外部,这两种结果会不一样的,解释如下:

(1)写在while外面的时候:比如你截图的这个代码功能应该是模拟售票的吧。比如有三个线程t1,t2,t3模拟1、2、3号窗口售票。如果你写在while外面的话,比如t2一旦拿到cpu执行权,并且判断synchronized(){}通过的话,就一直是t2窗口在卖票了,因为这时候t1和t3判断synchronized(){}是锁的都进不来,t2就一直在while循环,直到t2while循环完毕出来的时候,t1或者t3才能进去,这时候票已经卖完了。
说简单点就是,t1,t2,t3任意一个线程先拿到执行权的话就会一直是它在循环了。因为没有能停下来的语句,别的线程也执行不了,因为synchronized(){}是锁的。只有该线程循环完出来的时候,synchronized(){}才是打开的。这时候就不能满足t1,t2,t3随机进入while循环
(2)如果写在while里面就不同了,这三个线程不论是谁得到cpu执行权都会先进入while循环,然后判断synchronized(){}是否是锁的,如果不是就可以卖出一张票,如果是就等待别的线程先执行完。
(3)简单拿模拟窗口卖票来说,我们看到的执行效果的区别就是:
A:锁在while外面的时候,运行程序,不管有多少个窗口也总是一个窗口在卖票,从头卖到尾绝对永远都是先进入循环的那个线程会把所有票卖完
B:锁在while循环里面的时候,会是随机的一个窗口在卖,如果是三个线程的话,就应该是三个窗口都有在卖。

synchronized

首先synchronized就像一把锁,多个线程同时竞争synchronized代码块的资源,当一个线程先抢到这个资源时,就会上锁,别的线程就不能访问,只能等到当前线程执行完sychronized里面的代码才会释放锁,然后别的线程才可以竞争访问,接着又是上锁和释放锁的过程。 

上面这种写法是用synchronized修饰代码块,下面我用synchronized来修饰方法,还是同样的例子:

public class RunnableTicketDemo1 {

	public static void main(String[] args) {

		RunnableTicket1 runnable = new RunnableTicket1();
		Thread thread1 = new Thread(runnable,"窗口一");
		thread1.start();

		Thread thread2 = new Thread(runnable,"窗口二");
		thread2.start();
	}

}

class RunnableTicket1 implements Runnable{
	
	private int num = 10;
	
	// synchronized修饰方法
	public synchronized void saleTicket() {
		try {
			Thread.sleep(100);  //更好的体现结果
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		if (num > 0) {
			num --;
			if (num == 0) {
				System.out.println("票已卖完");
			} else {
				System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余" + num + "张票");
			}  
		}
	}
	
	@Override
	public void run() {
		
		while (true) {
			saleTicket();
		}
	}
}

和上面的代码等价

最后一个例子来模拟取钱,一个是从ATM取钱,一个从柜台存折取钱同时取:

public class ThreadBankDemo {

	public static void main(String[] args) {

		Bank bank = new Bank();
		BankThread p1 = new BankThread(bank);
		p1.start();
		
		BankThread p2 = new BankThread(bank);
		p2.start();
	}
}

class BankThread extends Thread{
	
	private Bank bank = null;

	public BankThread(Bank bank) {
		this.bank = bank;
	}
	@Override
	public void run() {
		System.out.println("取钱:" + bank.getMoney(400));
	}
	
}

class Bank{
	
	private int money = 500;
	
	// 取钱的方法,返回取钱的数目
	// 当一个线程去调用同步方法的时候,这个线程就获取了当前对象的锁
	// 其他线程当调用同步方法的时候只能等待,因为无法获取对象的锁
	//只有等第一个线程释放对象的锁方可进入
	public synchronized int getMoney(int number) {
		
		if (number < 0) {
			return -1;
		} else if (money < 0) {
			return -2;
		} else if (number - money > 0) {
			return -3;
		} else {
			try {
				Thread.sleep(100);  //更好的体现结果
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			money -= number;
			System.out.println("余额:" + money);
		}	
		return number;
	}
}

结果:

余额:100
取钱:400
取钱:-3

猜你喜欢

转载自blog.csdn.net/u013987258/article/details/82689100
今日推荐