本章要点:
- 多线程的运行原理
- 创建多线程的两种方式
- 能过说出多线程的六种状态
- 能够解决线程安全问题
多线程的运行原理
同一时间内,CPU只能处理1条线程,只有1条线程在工作(执行);多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)。如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。
创建多线程的两种方式:
1. 继承Thread类,调用start()方法
2. 实现Runnable接口
代码如下:
自定义线程类
/** * @author 阿豪 * @Date 2019-5-27
* 自定义线程类 */ public class ThreadDemo_01 extends Thread{ /** * 重写run方法,用于线程执行的操作 */ @Override public void run() { for (int i = 0; i < 20;i++) { System.out.println("自定义线程运行第 " + i + "次"); } } }
测试类
/** * @author 阿豪 * @Date 2019-5-27 * 测试类 */ public class Test { public static void main(String[] args) { //创建Thread对象 Thread thread = new ThreadDemo_01(); //开启自定义线程 thread.start(); //定义主线程执行操作 for (int i = 0; i < 20; i++) { System.out.println("主线程运行第" + i + "次"); } } }
线程安全问题
为什么会出现线程安全问题?
拿火车站售票举例:
假设总共100张火车票,总共有3个窗口(即3个线程)共同售票,当其中一个窗口已经卖完最后一张票的时候,其他的两个窗口并不知情,继续售卖,结果就会导致出现问题.所以就是线程不安全的问题
那么,如何解决线程安全问题呢?
有三种方法
1. 同步代码块
2. 同步方法
3. lock锁
1. 同步代码块:
synchronized(锁对象){
//可能会发生错误的代码
}
注意:
锁对象:
1. 锁对象必须被所有线程所共享,即锁对象是唯一的
2. 锁对象可以是任意对象
public class MyThread implements Runnable { //总共有100张火车票 private int ticket = 100; /** * 卖票的方法 */ @Override public void run() { while (true) { synchronized (MyThread.class) { if (ticket > 0) { try { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "票"); ticket--; } catch (InterruptedException e) { e.printStackTrace(); } } else { System.out.println("票卖完了"); break; } } } } }
2. 同步方法
将可能发生线程安全问题的代码封装成一个方法,这个方法要加上synchronized来修饰
public class MyThread implements Runnable { //总共有100张火车票 private int ticket = 100; boolean flag; /** * 卖票的方法 */ @Override public void run() { flag = true; while (flag) { method(); } } public synchronized void method(){ synchronized (MyThread.class) { if (ticket > 0) { try { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "票"); ticket--; } catch (InterruptedException e) { e.printStackTrace(); } } else { System.out.println("票卖完了"); flag = false; } } } }
3. 方式三:Lock锁
public class MyThread implements Runnable { //总共有100张火车票 private int ticket = 100; //创建look类的子类ReentrantLock, 然后在可能出现线程安全问题的代码前调用lock()方法 Lock look = new ReentrantLock(); /** * 卖票的方法 */ @Override public void run() { while (true) { //在可能发生线程安全问题的代码之前, 调用lock()方法 look.lock(); try { if (ticket > 0) { Thread.sleep(100); System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "票"); ticket--; } else { System.out.println("票卖完了"); break; } } catch (InterruptedException e) { e.printStackTrace(); } finally { //释放lock()方法 -- unlock() 放在可能发生线程安全问题的代码后 look.unlock(); } } } }
测试类:
/** * @author 阿豪 * @Date 2019-5-27 * 测试类 */ public class Test { public static void main(String[] args) { MyThread mt = new MyThread(); Thread t = new Thread(mt); Thread t2 = new Thread(mt); Thread t3 = new Thread(mt); t.start(); t2.start(); t3.start(); } }
多线程的六种状态:
1. NEW : 线程刚被创建, 但是未被启动
2. RUNNABLE : 可运行jvm里的状态
3. BLOCKED : 阻塞状态
4. WAITING : 无线等待
5. TIMEWAITING : 计时等待
6. TEMINATED : 被终止 (因run()方法执行完成, 正常终止)