多线程
一、什么是进程
进程是系统进行资源分配和调用的独立单元,每一个进程都有它的独立内存空间和系统资源。
二、单进程操作系统和多进程操作系统的区别
单进程操作系统:dos(一瞬间只能执行一个任务)
多进程单用户操作系统:Windows(一瞬间只能执行多个任务)
多进程多用户操作系统:Linux(一瞬间只能执行多个任务)
三、现在的多核CPU是否可以让系统在同一个时刻可以执行多个任务吗?
理论上是可以的
四、什么是线程,理解线程和进程的关系
什么是线程?
线程是进程里面的一条执行路径,每个线程同享进程里面的内存空间和系统资源
一个进程 可以有 多个线程:各个线程都有不同的分工
理解线程和进程的关系
进程 与 进程 之间的关系:进程之间的内存空间和系统资源是独立的
同一个进程里的多条线程 :线程之间的内存空间和系统资源是共享的
进程里:可以有一条或一条以上的线程
进程里只有一条线程的情况下,这条线程就叫做主线程
进程里有多条线程的情况下,只有一条线程叫做主线程
Ps:线程是在进程里的,他们是包含关系
五、我们应用的软件有哪些是多线程的应用?
都是
六、Java中,如何来编写多线程的应用程序?有哪些方法?
1.线程类
创建MyThread类,继承Thread,重写run方法
public class Test {
public static void main(String[] args) {
//创建线程的对象
MyThread t = new MyThread();
//启动线程
t.start();
}
}
//线程类
class MyThread extends Thread{
//当前线程抢到cpu资源后,就会执行run方法
@Override
public void run() {
System.out.println("当前线程抢到资源了");
}
}
2.任务类
创建Task类,实现Runnable接口中的run方法
public class Test {
public static void main(String[] args) {
Thread t = new Thread(new Task());
t.start();
}
}
//任务类
class Task implements Runnable{
//当前线程抢到cpu资源后,就会执行run方法
@Override
public void run() {
System.out.println("抢到资源了");
}
}
3.带返回值的任务类
4.线程池
七、感受多线程之间争抢资源的场景
需求:编写一个多线程的应用程序,主线程打印1-100之间的数字,子线程打印200-300之间的数字,观察其输出的结果,体会多线程互相争抢资源的场景
public class Test { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); for (int i = 1; i <= 100; i++) { System.out.println("主线程:" + i); } } } public class MyThread extends Thread{ @Override public void run() { for (int i = 200; i <= 300; i++) { System.out.println("子线程:" + i); } } }
截图:
八、小结
进程 与 进程 的关系:独享内存空间和系统资源
线程 与 进程 的关系:有一个进程中至少包含一个线程
线程 与 线程 的关系:在同一个进程里,多个线程共享内存空间和系统资源
一个进程中包含多个线程,只有一个主线程
当我们编写一个单纯的main方法时,此时该程序是否为单线程的?
垃圾回收器是一个后台线程
九、线程的优先级别
需求:在主线程中创3个子线程,并且设置不同优先级,观察其优先级对线程执行结果的”影响”。
a.setPriority(Thread.MAX_PRIORITY);//10
b.setPriority(Thread.NORM_PRIORITY);//5
c.setPriority(Thread.MIN_PRIORITY);//1优先级别:1~10(数字越大优先级越高)
线程的优先级只能影响抢到CPU资源的概率,不是决定性的!
十、给线程自定义名称
- 写getThreadName/setThreadName
- 调用父类的一个参数的构造方法和getName
- Thread.currentThread().getName()
十一、让线程休眠
需求:编写一个抽取学员回答问题的程序,要求倒数三秒后输出被抽中的学员姓名
import java.util.Random; public class Test { public static void main(String[] args) throws InterruptedException { String[] names = {"aaa","bbb","ccc","ddd","eee"}; 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]); } }
Thread.sleep(1000);
此方法为静态方法,写在哪个线程中,哪个线程就休眠
十二、线程的礼让
需求:创建两个线程A,B,分别各打印1-100的数字,其中B一个线程,每打印一次,就礼让一次,观察实验结果
public class Test { 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); Thread.yield(); } } }
Thread.yield();
此方法为静态方法,此方法写在哪个线程中,哪个线程就礼让
所谓的礼让是指当前线程退出CPU资源,并转到就绪状态,接着再抢
十三、线程的合并
需求:主线程和子线程各打印200次,从1开始每次增加1,当主线程打印到10之后,让子线程先打印完再打印主线程
public class Test01 { 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.join(); } } } } public class MyThread extends Thread{ @Override public void run() { for (int i = 1; i <= 200; i++) { System.out.println("子线程:" + i); } } }
t.join(); 合并方法
十四、线程的销毁
关闭线程
t.setFlag(false);
t.interrupt();//改变线程存活状态public class Test { public static void main(String[] args) throws InterruptedException { MyThread t = new MyThread(); t.start(); Thread.sleep(3000); //t.interrupt(); 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.setDaemon(true);
十六、线程局部变量(实现线程范围内的共享变量)
- 需求:共享单个数据
import java.util.concurrent.ConcurrentHashMap; public class Test { 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 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, 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(); } } //数据容器类 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(); 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); } }
十七、线程的生命周期
1、新建状态
i. 在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,它已经有了相应的内存空间和其它资源,但还处于不可运行状态。新建一个线程对象可采用线程构造方法来实现。
ii. 例如:Thread thread=new Thread();
2、 就绪状态
i. 新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待CPU调用,这表明它已经具备了运行条件。
3、运行状态
i. 当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()方法定义了该线程的操作和功能。
4、 阻塞状态
i. 一个正在执行的线程在某些特殊情况下,如被人为挂起,将让出CPU并暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(2000)、wait()等方法,线程都将进入阻塞状态。阻塞时,线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。
5、死亡状态
i. 线程调用stop()方法时或run()方法执行结束后,线程即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。