Day19
概念
一、什么是进程
进程是系统进行资源分配和调用的独立单元,每一个进程都有它的独立内存空间和系统资源。
二、单进程操作系统和多进程操作系统的区别
单进程操作系统:dos(一瞬间只能执行一个任务)
多进程单用户操作系统:Windows(一瞬间只能执行多个任务)
多进程多用户操作系统:Linux(一瞬间只能执行多个任务)
三、现在的多核CPU是否可以让系统在同一个时刻可以执行多个任务吗?
理论上是可以的
四、什么是线程,理解线程和进程的关系
什么是线程?
线程是进程里面的一条执行路径,每个线程同享进程里面的内存空间和系统资源
一个进程 可以有 多个线程:各个线程都有不同的分工
理解线程和进程的关系
进程 与 进程 之间的关系:进程之间的内存空间和系统资源是独立的
同一个进程里的多条线程 :线程之间的内存空间和系统资源是共享的
进程里:可以有一条或一条以上的线程
进程里只有一条线程的情况下,这条线程就叫做主线程
进程里有多条线程的情况下,只有一条线程叫做主线程
Ps:线程是在进程里的,他们是包含关系
五、我们应用的软件有哪些是多线程的应用?
都是
程序编写
一、创建线程
创建线程 – 线程类的方式
步骤:
1.创建MyThread类,继承Thread,重写父类的run方法
2.创建子线程对象
3.启动子线程//线程类 public class MyThread extends Thread{ //当前线程对象抢到cpu资源后调用的方法 @Override public void run() { System.out.println("子线程被调用了"); } }
public static void main(String[] args) { //创建子线程对象 MyThread t = new MyThread(); //启动子线程 t.start(); }
创建线程 – 任务类的方式
步骤:
1.创建Task,实现Runnable接口里的run方法
2.创建任务类对象
3.创建子线程对象,并把任务提交给子线程
4.启动子线程//任务类 public class Task implements Runnable{ //当前线程对象抢到CPU资源后调用的方法 @Override public void run() { System.out.println("任务被调用了"); } }
public static void main(String[] args) { //创建任务类对象 Task task = new Task(); //创建子线程对象,并把任务提交给子线程 Thread t = new Thread(task); //启动子线程 t.start(); }
二、多线程争抢资源场景
知识点:感受多线程之间争抢资源的场景
需求:编写一个多线程的应用程序,
主线程打印1-100之间的数字,
子线程打印200-300之间的数字,
观察其输出的结果,体会多线程互相争抢资源的场景public class MyThread extends Thread{ @Override public void run() { for (int i = 200; i <= 300; i++) { System.out.println("子线程:" + i); } } }
public static void main(String[] args) { MyThread t = new MyThread(); t.start(); for (int i = 1; i <= 100; i++) { System.out.println("主线程:" + i); } }
注意:主线程在调用main方法时就会开启,方法结束才关闭,因此开启子线程需要在for循环上面,否则就会主线程执行完for循环才开启子线程。
经典面试题:请问当我们编写一个单纯的main方法时,此时该程序是否为单线程的?为什么?
多线程,垃圾回收器是一个后台线程。
三、线程的优先级别
需求:在主线程中创3个子线程,并且设置不同优先级,观察其优先级对线程执行结果的”影响”。
优先级别:1~10(数字越大优先级越高)
注:线程的优先级只能影响抢到CPU资源的概率,不是决定性的!
public class A extends Thread{ @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println("A:" + i); } } } public class B extends Thread{ @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println("B:" + i); } } } public class C extends Thread{ @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println("C:" + i); } } }
public static void main(String[] args) { A a = new A(); B b = new B(); C c = new C(); a.setPriority(Thread.MAX_PRIORITY);//10 b.setPriority(Thread.NORM_PRIORITY);//5 c.setPriority(Thread.MIN_PRIORITY);//1 a.start(); b.start(); c.start(); }
关于该需求的代码可以进行优化,为避免相同功能线程类重复创建,可以给线程命名:
四、线程命名
//命名方法一:在子线程类中设置属性 public class MyThread extends Thread{ private String threadName; public MyThread(String threadName) { this.threadName = threadName; } @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println(threadName + ":" + i); } } }
//命名方法二:调用父类Thread的有参构造设置属性,并用父类的getName方法 public class MyThread extends Thread{ public MyThread(String name) { super(name); } @Override public void run() { for (int i = 1; i <= 100; i++) { // System.out.println(super.getName() + ":" + i); //Thread.currentThread()获取当前线程对象 System.out.println(Thread.currentThread().getName() + ":" + i); } } }
public static void main(String[] args) { MyThread a = new MyThread("A"); MyThread b = new MyThread("B"); MyThread c = new MyThread("C"); a.setPriority(Thread.MAX_PRIORITY);//10 b.setPriority(Thread.NORM_PRIORITY);//5 c.setPriority(Thread.MIN_PRIORITY);//1 a.start(); b.start(); c.start(); }
五、线程的生命周期
六、线程的休眠
需求:编写一个抽取学员回答问题的程序,要求倒数三秒后输出被抽中的学员姓名
Thread.sleep(1000); -> 让当前线程进入到阻塞状态,1000毫秒后进入到就绪状态
此方法为静态方法,写在哪个线程中,哪个线程就休眠
public static void main(String[] args) throws InterruptedException { String[] names = {"小宇","小亮","小丹","小蒲","雪飞","小康","小升","小怡"}; Random ran = new Random(); int index = ran.nextInt(names.length); for (int i = 3; i >= 1;i--) { System.out.println(i); Thread.sleep(1000); } System.out.println(names[index]); }
七、线程的礼让
需求:创建两个线程A,B,分别各打印1-100的数字,其中B一个线程,每打印一次,就礼让一次,观察实验结果
礼让:让当前线程退出CPU资源,该线程马上进入到就绪状态
Thread.yield() 写在哪个线程里,哪个线程就礼让
public static void main(String[] args) { A a = new A(); B b = new B(); a.start(); b.start(); }
public class A extends Thread{ @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println("A:" + i); } } }
public class B extends Thread{ @Override public void run() { for (int i = 1; i <= 100; i++) { System.out.println("B:" + i); //礼让:让当前线程退出CPU资源,该线程马上进入到就绪状态 Thread.yield(); } } }
八、知识点:线程的合并
需求:主线程和子线程各打印200次,从1开始每次增加1,当主线程打印到10之后,让子线程先打印完再打印主线程。
public static void main(String[] args) throws InterruptedException { MyThread t = new MyThread(); t.start(); for (int i = 1; i <= 200; i++) { System.out.println("主线程:" + i); if(i == 10){ //合并:让t线程加入到当前线程里 t.join(); } } }
public class MyThread extends Thread{ @Override public void run() { for (int i = 1; i <= 200; i++) { System.out.println("子线程:" + i); } } }
九、知识点:线程的销毁
用stop()方法(不推荐,不知道执行到线程哪个地方)
public static void main(String[] args) throws InterruptedException { MyThread t = new MyThread(); t.start(); // 主线程休眠 3 秒,以便让 MyThread 运行一段时间 Thread.sleep(3000); //关闭线程 - 立即关闭 t.stop(); }
public class MyThread extends Thread{ @Override public void run() { while(true){ System.out.println("111"); System.out.println("222"); System.out.println("333"); System.out.println("444"); } } }
设置flag
public static void main(String[] args) throws InterruptedException { MyThread t = new MyThread(); t.start(); Thread.sleep(3000); //关闭线程 t.setFlag(false); }
public class MyThread extends Thread{ private boolean flag = true; @Override public void run() { while(flag){ System.out.println("111"); System.out.println("222"); System.out.println("333"); System.out.println("444"); } } public void setFlag(boolean flag) { this.flag = flag; } }
t.interrupt() - 改变线程存活状态
Thread.currentThread().isInterrupted() - 判断线程存货状态(false-未销毁 true-销毁)
public class Test01 { /** * 知识点:线程的销毁 */ public static void main(String[] args) throws InterruptedException { MyThread t = new MyThread(); t.start(); Thread.sleep(3000); //关闭线程 t.interrupt();//改变线程存活状态 } }
public void run() { //Thread.currentThread().isInterrupted() - 判断线程存货状态(false-未销毁 true-销毁) while(!Thread.currentThread().isInterrupted()){ System.out.println("111"); System.out.println("222"); System.out.println("333"); System.out.println("444"); } }
十、守护线程
知识点:守护线程
理解:默默守护着前台线程,当所有的前台线程都消亡后,守护线程会自动消亡
注意:垃圾回收器就是守护线程
public class MyThread extends Thread{ @Override public void run() { while(true){ System.out.println("后台线程默默守护着前台线程"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
注意:为什么这里不能throws?因为父类没有抛异常,子类就不能抛。
public static void main(String[] args) throws InterruptedException { MyThread t = new MyThread(); t.setDaemon(true);//将当前线程设置为守护线程 t.start(); for (int i = 1; i <= 5; i++) { System.out.println("主线程:" + i); Thread.sleep(1000); } }
十一、线程局部变量(实现线程范围内的共享变量)
需求:共享单个数据
public class A { public void println(){ Thread thread = Thread.currentThread(); Integer value = Test01.map.get(thread); System.out.println(thread.getName() + "里的A类对象获取了数据:" + value); } } public class B { public void println(){ Thread thread = Thread.currentThread(); Integer value = Test01.map.get(thread); System.out.println(thread.getName() + "里的B类对象获取了数据:" + value); } } import java.util.concurrent.ConcurrentHashMap; public class Test01 { /** * 知识点:线程局部变量共享 * * 需求:共享单个数据 */ public static ConcurrentHashMap<Thread, Integer> map = new ConcurrentHashMap<>(); public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { int i = 10; map.put(Thread.currentThread(), i); A a = new A(); B b = new B(); a.println();//10 b.println();//10 } }, "线程1").start(); new Thread(new Runnable() { @Override public void run() { int i = 20; map.put(Thread.currentThread(), i); A a = new A(); B b = new B(); a.println();//20 b.println();//20 } }, "线程2").start(); } }
共享多个数据
//数据容器类 public class Data { private int i; private String str; public Data(int i, String str) { this.i = i; this.str = str; } public int getI() { return i; } public void setI(int i) { this.i = i; } public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return i + " -- " + str; } } public class A { public void println(){ Thread thread = Thread.currentThread(); Data value = Test01.map.get(thread); System.out.println(thread.getName() + "里的A类对象获取了数据:" + value); } } public class B { public void println(){ Thread thread = Thread.currentThread(); Data value = Test01.map.get(thread); System.out.println(thread.getName() + "里的B类对象获取了数据:" + value); } }
public static ConcurrentHashMap<Thread, Data> map = new ConcurrentHashMap<>(); public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { int i = 10; String str = "用良心"; Data data = new Data(i, str); map.put(Thread.currentThread(), data); A a = new A(); B b = new B(); a.println();//10 b.println();//10 } }, "线程1").start(); new Thread(new Runnable() { @Override public void run() { int i = 20; String str = "做自己"; Data data = new Data(i, str); map.put(Thread.currentThread(), data); A a = new A(); B b = new B(); a.println();//20 b.println();//20 } }, "线程2").start(); }
需求:共享多个数据 - 使用ThreadLocal
//数据容器类 public class Data { private int i; private String str; private Data(int i, String str) { this.i = i; this.str = str; } //该方法保证了当前线程只能有一个Data对象 public static Data getIntance(int i ,String str){ //获取当前线程的Data对象 Data data = Test01.local.get(); //判断data对象是否存在 if(data == null){ data = new Data(i, str); }else{ data.setI(i); data.setStr(str); } return data; } public int getI() { return i; } public void setI(int i) { this.i = i; } public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return i + " -- " + str; } } public class A { public void println(){ Thread thread = Thread.currentThread(); Data value = Test01.local.get(); System.out.println(thread.getName() + "里的A类对象获取了数据:" + value); } } public class B { public void println(){ Thread thread = Thread.currentThread(); Data value = Test01.local.get(); System.out.println(thread.getName() + "里的B类对象获取了数据:" + value); } }
public static ThreadLocal<Data> local = new ThreadLocal<>(); public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { int i = 10; String str = "用良心做教育"; Data data = Data.getIntance(i, str); local.set(data); A a = new A(); B b = new B(); a.println();//10 b.println();//10 } }, "线程1").start(); new Thread(new Runnable() { @Override public void run() { int i = 20; String str = "做真实的自己"; Data data = Data.getIntance(i, str); local.set(data); A a = new A(); B b = new B(); a.println();//20 b.println();//20 } }, "线程2").start(); }
练习
1.计算任务,一个包含了2万个整数的数组,分拆了多个线程来进行并行计算,最后汇总出计算的结果。
2.铁道部发布了一个售票任务,要求销售1000张票,要求有3个窗口来进行销售,请编写多线程程序来模拟这个效果(该题涉及到线程安全,https://www.jb51.net/article/221008.htm)
i. 窗口001正在销售第1张票
ii. 窗口001正在销售第2张票
iii. 窗口002正在销售第3张票
iv. 。。。
v. 窗口002正在销售第1000张票
涉及到线程安全,要加锁