多线程
1.进程
进行中的应用程序,只有一个应用程序处于运行状态,才能被称之为进程。 应用程序的执行实例,有独立的内存空间和系统资源。
2. 线程
CPU执行的最小单位,包含在进程之中。 CPU调度和分派的基本单位,应用程序运算是最小单位。
3. 进程和线程的关系
进程和线程的关系,就像车身和车轮的关系,不是越多越好,要根据实际的硬件环境,选择最合适的数量,才是最优方案。
4. CPU和线程的执行
单核心CPU下,多线程是轮流交替执行。每个任务最多执行20ms,每个任务准备好以后,会向CPU发出指令:准备好了 真正是否能够被执行 不确定 因为同时可能有很多线程都准备好了
5. 并发和并行
并发是指同时发生,轮流交替来执行 并行是真正意义 上的同时执行
6. 线程的创建
6.1 继承Thread类
线程的创建方式1:继承Thread类 重写run方法
package com.qfedu.test2;/*** 线程的创建方式1:继承Thread类 重写run方法* @author WHD**/public class T1 extends Thread{@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}public static void main(String[] args) {T1 t1 = new T1();t1.setName("线程A");t1.start(); // 调用start方法才会开启新的线程// t1.run(); 调用run方法不会开启新的线程 依然使用main线程调用run方法}}
6.2 实现Runnable接口
创建线程方式2 :实现Runnable接口 重写run方法
package com.qfedu.test2;/*** 创建线程方式2 :实现Runnable接口 重写run方法* @author WHD**/public class T2 implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}public static void main(String[] args) {T2 t2 = new T2();Thread thread = new Thread(t2, "线程A");thread.start();}}
6.3 两种方式对比
继承Thread类 编写简单,可直接操作线程 适用于单继承 实现Runnable接口 避免单继承局限性 便于共享资源 推荐使用实现Runnable接口方式创建线程
7. 线程的状态
线程的状态 创建—就绪—运行—阻塞—死亡
package com.qfedu.test3;/*** 线程的状态* 创建--就绪--运行--阻塞--死亡* @author WHD**/public class T1 extends Thread{@Overridepublic void run() { // 运行try {Thread.sleep(2000); // 阻塞} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName());}// 死亡public static void main(String[] args) {T1 t1 = new T1(); // 创建t1.setName("线程A");t1.start(); // 就绪}}
8. 线程的优先级
线程的优先级 默认为5 最低为1 最高为10 优先级高代表获取CPU的概率较大 并不能保证一定会优先获得CPU资源 MAX_PRIORITY 最高10 MIN_PRIORITY 最低1 NORM_PRIORITY 默认5
package com.qfedu.test4;/*** 线程的优先级 默认为5 最低为1 最高为10* 优先级高代表获取CPU的概率较大 并不能保证一定会优先获得CPU资源* @author WHD**/public class T1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + i);}}public static void main(String[] args) {T1 thread1 = new T1();thread1.setName("赵四");T1 thread2 = new T1();thread2.setName("广坤");System.out.println(thread1.getPriority());System.out.println(thread2.getPriority());thread1.setPriority(Thread.MAX_PRIORITY); // 设置优先级为最高 10thread2.setPriority(1);// 设置优先级为最低 1thread1.start();thread2.start();}}
9. 线程的休眠
线程休眠的方法 sleep(long mills)
package com.qfedu.test5;/*** 线程休眠的方法 sleep(long mills)** @author WHD**/public class T1 extends Thread{@Overridepublic void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName());}public static void main(String[] args) {T1 t1 = new T1();t1.setName("线程A");t1.start();}}
10. 线程的插队
线程的插队 join() 等待插队线程执行完毕 再执行当前线程 join(long mills) 等待插队线程固定时间 时间结束就执行当前线程
package com.qfedu.test5;/*** 线程的插队* join() 等待插队线程执行完毕 再执行当前线程* join(long mills) 等待插队线程固定时间 时间结束就执行当前线程* @author WHD**/public class T2 extends Thread{@Overridepublic void run() {for (int i = 0; i < 50; i++) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + i);}}public static void main(String[] args) throws InterruptedException {T2 t2 = new T2();t2.setName("--------线程A--------");t2.start();for (int i = 0; i < 20; i++) {if(i == 10) {t2.join(1000);}System.out.println(Thread.currentThread().getName() + i);}}}
11. 线程的礼让
yield()方法 线程礼让 当前线程礼让其他线程 让其他线程先执行 只是提供一种可能 不一定会礼让
package com.qfedu.test6;/*** yield()方法 线程礼让 当前线程礼让其他线程 让其他线程先执行* 只是提供一种可能 不一定会礼让* @author WHD**/public class T1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 20; i++) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if(i == 5) {Thread.yield();System.out.println(Thread.currentThread().getName() + "礼让了------------------");}System.out.println(Thread.currentThread().getName() + i);}}public static void main(String[] args) {T1 thread1 = new T1();T1 thread2 = new T1();thread1.setName("线程A");thread2.setName("线程B");thread1.start();thread2.start();}}
12.线程的中断
interrupt() 中断线程方法 但不是立即中断 如果当前线程有未执行完的任务 将执行完毕再中断 interrupted() 查看当前线程是否可以被中断 true为是 false为否 stop() 为立即中断线程的方法
package com.qfedu.test6;/*** interrupt() 中断线程方法 但不是立即中断 如果当前线程有未执行完的任务 将执行完毕再中断* interrupted() 查看当前线程是否可以被中断 true为是 false为否* stop() 为立即中断线程的方法* @author WHD**/public class T2 extends Thread{@Overridepublic void run() {for (int i = 0; i < 20; i++) {if(i == 10) {System.out.println("执行了线程中断方法interrupt()");Thread.currentThread().interrupt();// boolean flag = Thread.interrupted();// System.out.println(flag);}System.out.println(Thread.currentThread().getName() + i);}}public static void main(String[] args) throws InterruptedException {T2 t2 = new T2();t2.setName("线程A");t2.start();Thread.sleep(2000);System.out.println(t2.isAlive()); // 查看t2线程是否存活}}
13.线程安全
多线程共享数据引发的问题
package com.qfedu.test8;/*** 因为T1类中count是实例类型的 所以每new一个对象 就存在一份拷贝 有三个count = 10* 所以我们将count改为static修饰的* 修改完以后:* 1.同一张票重复卖出* 2.在run方法中添加线程休眠 发现票的数量有误* @author WHD**/public class T2 extends Thread{private static int count = 10;@Overridepublic void run() {while(count > 0) {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}count--;System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - count)+" 张票,还剩余" + count + "张票");}}public static void main(String[] args) {T2 zhaosi = new T2();T2 guangkun = new T2();T2 dana = new T2();zhaosi.setName("赵四");guangkun.setName("广坤");dana.setName("大拿");zhaosi.start();guangkun.start();dana.start();}}
解决方案:使用同步代码块
package com.qfedu.test8;/**** @author WHD**/public class T4 implements Runnable{int count = 10;@Overridepublic void run() {while(true ) {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}synchronized(this) {if(count == 0) {break;}count--;System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - count)+" 张票,还剩余" + count + "张票");}}// 后续代码}public static void main(String[] args) {T4 t3 = new T4();Thread zhaosi = new Thread(t3, "赵四");Thread guangkun = new Thread(t3, "广坤");Thread dana = new Thread(t3, "大拿");zhaosi.start();guangkun.start();dana.start();}}
使用同步方法实现
package com.qfedu.test8;/*** synchronized修饰方法 表示此方法同一时间只能有一个线程访问* @author WHD**/public class T5 implements Runnable{int count = 10;@Overridepublic synchronized void run() {while(count > 0) {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}count--;System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - count)+" 张票,还剩余" + count + "张票");}// 后续代码}public static void main(String[] args) {T5 t3 = new T5();Thread zhaosi = new Thread(t3, "赵四");Thread guangkun = new Thread(t3, "广坤");Thread dana = new Thread(t3, "大拿");zhaosi.start();guangkun.start();dana.start();}}
14.synchronized关键字规则
同一时刻只能有一个线程进入synchronized(this)同步代码块
当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
