第一章 多线程概述
1 2 3 | 1. 什么是程序? 2. 什么是进程? 3. 什么是线程? |
- 程序
1 | 是为完成特定任务、用某种语言编写的一组指令的集合(一段静态的代码) |
- 进程
1 | 是程序的一次执行过程,或是正在运行的一个程序 |
- 线程
1 2 3 | 进程可进一步细化为线程,是一个程序内部的一条执行路径 若一个程序可同一时间执行多个线程,我们称之为多线程. |
- 多线程的使用
1 2 3 4 5 | 我们在什么情况下使用多线程呢? 1. 程序需要同时执行多个任务的时候(2个或2个以上) 2. 程序需要实现一些需要等待的任务时(例如360杀毒软件中有很多功能,但是这些功能都没有被调用,都在等待被调) 3. 后台运行的程序 |
第二章 多线程创建和使用
1 2 3 | Java线程创建有两种方式 1. 一种是继承Thread类 2. 一种是实现Runnable接口 |
第1节 继承Thread类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class TestDemo1 { public static void main(String[] args) { //创建线程 MyThread mt = new MyThread(); //启动线程 mt.start(); } } /** * 编写自定义类实现Thread类,重写run方法 * run方法由jvm虚拟机调用,待抢到CPU资源之后才会执行 */ class MyThread extends Thread{ @Override public void run() { System.out.println("我被线程调用了..."); } } |
第2节 实现Runnable接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class TestDemo2 { public static void main(String[] args) { //创建MyRunnable实例 MyRunnable mr = new MyRunnable(); //创建线程 Thread t = new Thread(mr); //启动 t.start(); } } class MyRunnable implements Runnable{ @Override public void run() { System.out.println("我被线程调用了..."); } } |
第三章 线程的优先级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * Thread类中自带优先级设置 最小优先级 */ public final static int MIN_PRIORITY = 1; /** * Thread类中自带优先级设置 默认优先级 */ public final static int NORM_PRIORITY = 5; /** * Thread类中自带优先级设置 最大优先级 */ public final static int MAX_PRIORITY = 10; |
1 2 3 4 5 6 | Thread类中提供了设置和获取优先级的方法 setPriority(): 设置优先级(取值范围1-10) getPriority(): 获取优先级方法 在线程中设置线程优先级不会出现明显的变化,优先级高的话只是分配到CPU资源的概率高. |
第四章 Thread类常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 | start():线程启动方法 run(): 线程被调度时执行的方法 getName(): 返回线程名称 setName(String name): 设置线程名称 currentThread(): 返回当前线程 yield(): 线程让步 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程 若队列中没有同优先级的线程,忽略此方法 线程让步,不代表暂停执行,就像你在高速开车,让了路,不代表车停下来,让给其他线程,CPU可能还会分配给它资源. join():当某个程序执行中调用其他线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完为止.低优先级的线程也可以获得执行 sleep(long millis): 令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队 stop(): 强制线程生命期结束(已过时) isAlive(): 返回boolean,判断线程是否还活着 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //join测试 public class TestDemo01 { public static void main(String[] args) throws InterruptedException { TaskRnnable t = new TaskRnnable(); Thread t1 = new Thread(t, "线程1"); Thread t2 = new Thread(t, "线程2"); t1.start(); t1.join();//join之后必须t1结束之后才能执行t2 t2.start(); } } class TaskRnnable implements Runnable{ @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"执行了...."); } } |
第五章 线程的生命周期
1 | 线程的生命周期状态: 创建 -- 就绪 -- 运行 -- 停止 -- 阻塞 |
第六章 线程同步
1 2 3 4 5 6 7 8 9 | 因为多线程执行的不确定性引起执行结果的不稳定性,可能造成数据出现问题. Java中引入互斥锁的概念,来保证共享数据操作的完整性.每个对象都对应于一个可称为"互斥锁" 的标记;这个标记用来保证在任一时刻,只能有一个线程访问该对象. 在Java中使用synchronized关键字来实现互斥锁 1. 同步代码块 锁对象: 1.1 实例对象(锁实例数据) 1.2 静态对象(锁静态数据) 2. 同步方法 2.1 实例方法(锁为this) 2.2 静态方法(锁为当前类本身) |
模拟火车站售票程序,开启三个窗口售票
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //通过这个案例,多次执行会发现他的数据执行顺序是有问题的. public class TestDemo3 { public static void main(String[] args) { //创建车票 Ticket ticket = new Ticket(); //创建线程并启动 new Thread(ticket,"win1").start(); new Thread(ticket,"win2").start(); new Thread(ticket,"win3").start(); } } class Ticket implements Runnable{ private int ticket=100; @Override public void run() { while(true){ if(ticket>0){ System.out.println("窗口-> "+ Thread.currentThread().getName() +" <- 买票"+ticket+"剩余票数为:"+ --ticket); }else { break; } } } } |
解决问题
- 同步代码块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | synchronized (锁对象){ //需要锁定的资源 } public class TestDemo3 { public static void main(String[] args) { //创建车票 Ticket ticket = new Ticket(); //创建线程并启动 new Thread(ticket,"win1").start(); new Thread(ticket,"win2").start(); new Thread(ticket,"win3").start(); } } class Ticket implements Runnable{ private int ticket=100; @Override public void run() { while(true){ //锁定共享资源 synchronized (this){ if(ticket>0){ System.out.println("窗口-> "+ Thread.currentThread().getName() +" <- 买票"+ticket+"剩余票数为:"+ --ticket); }else { break; } } } } } |
- 同步方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //使用synchronized修饰操作共享资源的方法 public class TestDemo4 { public static void main(String[] args) { //创建车票 Ticket1 ticket = new Ticket1(); //创建线程并启动 new Thread(ticket,"win1").start(); new Thread(ticket,"win2").start(); new Thread(ticket,"win3").start(); } } class Ticket1 implements Runnable{ private int ticket=100; @Override public void run() { while(true){ sale(); } } //给操作共享资源的方法添加synchronized public synchronized void sale(){ if(ticket>0){ System.out.println("窗口-> "+ Thread.currentThread().getName() +" <- 买票"+ticket+"剩余票数为:"+ --ticket); }else{ return; } } } |
第七章 死锁
1 | 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | public class DeadLock implements Runnable { private static Object obj1 = new Object(); private static Object obj2 = new Object(); private boolean flag; public DeadLock(boolean flag) { this.flag = flag; } @Override public void run() { if(flag) { synchronized (obj1) { System.out.println(Thread.currentThread().getName()+"已经锁定obj1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //如果出现死锁当前执行不到 synchronized (obj2) { System.out.println("一秒钟后"+Thread.currentThread().getName()+"已经锁定obj2"); } } }else { synchronized (obj2) { System.out.println(Thread.currentThread().getName()+"已经锁定obj2"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //如果死锁访问不到 synchronized (obj1) { System.out.println("一秒钟后"+Thread.currentThread().getName()+"已经锁定obj1"); } } } } } //main方法 /** * 死锁 * * obj1 * obj2 * * A线程 * run(){ * synchronized(obj1){ * //代码 * synchronized(obj2){ * // * } * } * } * B线程 * run(){ * synchronized(obj2){ * //代码 * synchronized(obj1){ * // * } * } * } */ public class TestDemo01 { public static void main(String[] args) { //new Thread(new DeadLock(true)).start(); new Thread(new DeadLock(false)).start();//死锁 new Thread(new DeadLock(true)).start(); } } |
第八章 线程通信
第1节 为什么要进行线程通信
多个线程并发执行时,在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务.并且我们希望他们有规律的执行,那么多线程之间需要一些协调通信 |
第2节 Java语言实现通信的方式
java.lang.Object类中提供了wait()/notify/notifyAll()方法实现线程之间的通信;这三个方法必须在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常 |
- wait()方法
当前线程挂起并放弃CPU,释放对锁的拥有权(在wait时必须先获取锁,所以wait必须在synchronized中),同时在等待的位置加一个标志,以备后面被唤醒时它好能从标志位置获得锁的拥有权,变成就绪状态. |
- notify()方法
1 | 唤醒一个等待当前对象的锁的线程 |
- notifyAll()方法
1 | 方法会唤醒在此对象监视器上等待的所有线程 |
第3节 练习(使用两个线程打印 1-100 线程1, 线程2 交替打印)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class PrintNum implements Runnable{ int num=1; @Override public void run() { while(true) { synchronized (this) { notify();//唤醒 try { Thread.sleep(100); } catch (InterruptedException e1) { e1.printStackTrace(); } if(num<=100) { System.out.println(Thread.currentThread().getName()+"-->"+ num); num++; try { //等待 wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { break; } } } } } public static void main(String[] args) { /* * 创建两个线程,循环1-100 */ PrintNum p = new PrintNum(); new Thread(p).start(); new Thread(p).start(); } |
第九章 生产着消费者模式
- 容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public class Box { //设置盒子容量 private int c=0; //最大值10 /** * 向盒子中添加对象 */ public synchronized void add() { if(c>=10) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { System.out.println("生产产品"+c); c++; notifyAll(); } } /** * 获取 */ public synchronized void get() { if(c<=0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { System.out.println("消费产品"+c); c--; notifyAll(); } } } |
- 生产者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* * 生产线程 */ public class Pro implements Runnable{ Box box; public Pro(Box box) { this.box = box; } @Override public void run() { System.out.println("生产开始...."); while(true) { try { Thread.sleep((int)Math.random()*1000); } catch (InterruptedException e) { e.printStackTrace(); } box.add(); } } } |
- 消费者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /** * 消费线程 */ public class Cus implements Runnable { Box box; public Cus(Box box) { this.box = box; } @Override public void run() { System.out.println("消费开始..."); while(true) { try { Thread.sleep((int)Math.random()*1000); } catch (InterruptedException e) { e.printStackTrace(); } box.get(); } } } |
- 测试
1 2 3 4 5 6 7 8 9 10 11 12 | public static void main(String[] args) { //创建容器 Box box = new Box(); //创建生产者 Pro pro = new Pro(box); //创建消费者 Cus cus = new Cus(box); //给生产者分配线程 new Thread(pro).start(); //给消费者分配线程 new Thread(cus).start(); } |