第13章_多线程
一、 多线程相关的概念:
- 程序:由某种编程语言开发可执行某些功能的代码组合,它是静态的概念。
- 进程:当程序被执行时的过程可以理解为讲程序从外存调入内存的过程,会为每一个程序至少开辟一个独立的内存空间,程序在内存中的状态称为一个进程。
- 线程:一个进程至少会有一个独立的内存空间,但线程是依附在进程的内存空间中工作的,因此它没有自己的独立内存空间,多个线程会共用一个进程的内存空间。
- 多线程开发:往往一个进程中可以包含多个线程,多线程开发就是要具体实施一个进程中执行(启动)多个线程。
二、 Java中如何实现多线程开发:
- 通过继承Thread类,并重写Run方法完成多线程开发。
当一个类继承Thread父类就可以通过该类调用start()方法启动线程,创建多个对象就可以启动多个线程,run()方法是在启动线程时由JVM调用执行。
代码参考:com.oop.ch13.ThreadTest
package com.oop.ch13; /** * 练习通过继承Thread类,并重写Run()方法完成多线程开发。 * */ public class ThreadTest { public static void main(String[] args) { Person person = new Person(); person.start(); Person person1 = new Person(); person1.start(); Person person2 = new Person(); person2.start(); } } class Person extends Thread { @Override public void run() { Integer nums = 1; while (true) { if (nums <= 5) { System.out.println(Thread.currentThread().getName() + "线程第" + nums + "次输出"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } nums++; } else { System.out.println("执行完成"); break; } } } }
- 通过实现Runnable接口,并实现Run方法完成多线程开发。
因为Java是一个单继承的编程语言,因此为了完成多线程开发,我们可能通常会直接实现Runnable接口,而不是继承Thread父类。
当一个类实现Runnable接口后,在创建Thread对象时可以将实现了Runnable的实现类对象作为参数,从而通过Thread的对象来启动多个线程。
代码参考:com.oop.ch13.RunnableTest
package com.oop.ch13; /** * 练习:通过实现Runnable接口,并实现Run方法完成多线程开发。 * */ public class RunnableTest { public static void main(String[] args) { Animal animal = new Animal(); //将Animal的对象作为参数传给Thread Thread thread1 = new Thread(animal); thread1.start(); Thread thread2 = new Thread(animal); thread2.start(); Thread thread3 = new Thread(animal); thread3.start(); } } class Animal implements Runnable{ @Override public void run() { Integer nums = 1; while (true) { if (nums <= 5) { System.out.println(Thread.currentThread().getName() + "线程第" + nums + "次输出"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } nums++; } else { System.out.println("执行完成"); break; } } } }
三、 解决多线程开发中存在的问题:
问题:在进行多线程开发时会存在线程安全的问题(同步问题),多个线程共用一个进程的内存空间,访问的变量、方法都是同一个。
解决方案:使用一个解决同步问题的关键字synchronized,具体使用方式有两种。
方式1:使用synchronized关键字修饰方法,方法就为同步方法;
方式2:使用synchronized关键字修饰一段代码块,编写一段同步代码块。
案例描述:模拟“机票销售系统”,多个窗口会销售同一个航班的票,要保证机票不能出现多个窗口销售同一张机票的情况。
代码参见:
com.oop.ch13.WindowTest
package com.oop.ch13; /** * 模拟“机票销售系统”,多个窗口会销售同一个航班的票,要保证机票不能出现多个窗口销售同一张机票的情况。 * 没有进行同步处理。 * */ public class WindowTest { public static void main(String[] args) { Window window = new Window(); Thread thread1 = new Thread(window); thread1.start(); Thread thread2 = new Thread(window); thread2.start(); Thread thread3 = new Thread(window); thread3.start(); } } class Window implements Runnable{ @Override public void run() { Integer nums = 20; while(true) { if (nums >0) { System.out.println(Thread.currentThread().getName() + "销售了剩余的第" + nums + "张票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } nums --; }else { System.out.println("机票销售完成"); break; } } } }
com.oop.ch13.WindowSynchronizedTest
package com.oop.ch13; /** * 模拟“机票销售系统”,多个窗口会销售同一个航班的票,要保证机票不能出现多个窗口销售同一张机票的情况。 * * 进行同步处理。 * */ public class WindowSynchronizedTest { public static void main(String[] args) { Windows windows = new Windows(); Thread thread1 = new Thread(windows); thread1.start(); Thread thread2 = new Thread(windows); thread2.start(); Thread thread3 = new Thread(windows); thread3.start(); } } class Windows implements Runnable { //声明为成员变量,这样多个线程共用的才是同一个变量,否则每个线程都会运行一遍run()方法 Integer nums = 20; @Override /* * 用synchronized关键字修饰一段代码块 * 或用synchronized修饰方法,就是同步方法 * public void synchronized run(){} * */ public void run() { while (true) { //同步处理 synchronized (this) { if (nums > 0) { System.out.println(Thread.currentThread().getName() + "销售了剩余的第" + nums + "张票"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } nums--; } else { System.out.println("机票销售完成"); break; } } } } }
四、 线程的五种状态:
1、 创建(New):
2、 就绪(Runnable):
3、 运行(Running):
4、 阻塞(Blocked):
5、 死亡(Dead):