多线程(多窗口卖票例子)
实现多线程的方式:
实现多线程的方式有多种,这里只列举两种常用的,而第一种继承Thread的方式无法实现多窗口卖票。
一,继承Thread方式:
特点:多线程多实例,无法实现资源的共享。
例子:
1 package com.demo.study.multithreading; 2 3 public class MyThread extends Thread{ 4 5 private int i = 10; 6 // 可以自行定义锁,也可以使用实例的锁 7 Object mutex = new Object(); 8 public void selltickets(){ 9 10 synchronized (mutex) { 11 12 if(i>0){ 13 i--; 14 //getName()获取线程的名字 15 System.out.println(Thread.currentThread().getName()+" :"+ i); 16 } 17 } 18 } 19 20 @Override 21 public void run() { 22 while(i>0){ 23 24 selltickets(); 25 } 26 } 27 }
启动线程:
1 package com.demo.study.multithreading; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 //继承Thread方式:多线程多实例,无法实现资源的共享 7 MyThread myThread1 = new MyThread(); 8 MyThread myThread2 = new MyThread(); 9 //给线程命名 10 myThread1.setName("线程1"); 11 myThread2.setName("线程2"); 12 myThread1.start(); 13 myThread2.start(); 14 } 15 }
运行结果:
二,实现Runnable方式:
特点:多线程单实例,可实现资源的共享
例子:实现多窗口卖票:
1 package com.demo.study.multithreading; 2 3 public class MyThreadImpl implements Runnable { 4 5 private int tickets = 10; 6 7 public void sellTickets() { 8 9 synchronized (MyThreadImpl.class) { 10 if (tickets > 0) { 11 12 tickets--; 13 System.out.println(Thread.currentThread().getName() + "正在卖票,还剩下" + tickets + "张"); 14 } 15 } 16 } 17 18 @Override 19 public void run() { 20 21 while (tickets > 0) { 22 sellTickets(); 23 try { 24 // 休眠一秒,让执行的效果更明显 25 Thread.sleep(100); 26 } catch (InterruptedException e) { 27 e.printStackTrace(); 28 } 29 } 30 } 31 }
启动线程:
注意:Thread中的start()方法是线程的就绪,而线程的启动,需要等待CPU的调度(也就是所谓的线程抢资源);run()方法的方法体代表了线程需要完成的任务,称之为线程执行体。
void |
start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
1 package com.demo.study.multithreading; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 7 //只创建一个实例 8 MyThreadImpl threadImpl = new MyThreadImpl(); 9 //将上面创建的唯一实例放入多个线程中,Thread类提供了多个构造方法,见下图(构造方法摘要) 10 Thread thread1 = new Thread(threadImpl, "窗口1"); 11 Thread thread2 = new Thread(threadImpl, "窗口2"); 12 thread1.start(); 13 thread2.start(); 14 15 } 16 }
构造方法摘要 | |
---|---|
Thread() 分配新的 Thread 对象。 |
|
Thread(Runnable target)
分配新的 Thread 对象。 |
|
Thread(Runnable target,
String name) 分配新的 Thread 对象。 |
|
Thread(String name)
分配新的 Thread 对象。 |
|
Thread(ThreadGroup group, Runnable target)
分配新的 Thread 对象。 |
|
Thread(ThreadGroup group, Runnable target,
String name) 分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name
作为其名称,并作为 group 所引用的线程组的一员。 |
|
Thread(ThreadGroup group, Runnable target,
String name,
long stackSize) 分配新的 Thread 对象,以便将
target 作为其运行对象,将指定的 name 作为其名称,作为 group
所引用的线程组的一员,并具有指定的堆栈大小。 |
|
Thread(ThreadGroup group, String name)
分配新的 Thread 对象。 |
运行结果:
三、同步锁与资源共享:
CPU可能随机的在多个处于就绪状态中的线程中进行切换,这时就可能出现线程的安全问题;线程安全问题,其实是指多线程环境下对共享资源的访问可能会引起此共享资源的不一致性,而解决安全问题则需要同步锁的加入,执行synchronized部分代码的时候必须需要对象锁,而一个对象只有一个锁,只有执行完synchronized里面的代码后释放锁,其他线程才可以获得锁,那么就保证了同一时刻只有一个线程访问synchronized里面的代码。实现资源共享的关键是,只有一个实例,synchronized使用的是同一把锁,用实例的锁或者定义一个实例。这就需要使用实现Runnable接口的方式,实现多线程,这样传入的是一个实例。继承Thread的方式,传入的是多个实例,每个实例都有一个锁,那就无法实现控制。