1. 什么是进程?什么是程序?有什么区别?
程序:数据与指令的集合,程序是静态的 进程:给程序加入了时间的概念,不同的时间进程有不同的状态 进程是动态的,就代表OS中正在运行的程序 独立性,动态性,并发性
2. 什么是并行?什么是串行?什么是并发?
CPU:电脑的核心处理器,类似于“大脑” 串行:是指同一时刻一个CPU只能处理一件事,类似于单车道 并行:相对来说资源比较充足,多个CPU可以同时处理不同的多件事,类似于多车道 并发:相对来说资源比较紧缺,多个进程同时抢占公共资源,比如多个进程抢占一个CPU
3. 什么是线程?线程与进程有什么关系?
线程是OS能够进行运算调度的最小单位 一个进程可以拥有多个线程,当然,也可以只拥有一个线程,只有一个线程的进程被称作单线程程序 注意:每个线程也有自己独立的内存空间,当然也有一部分公共的空间用于保存共享的数据
在宏观上,一个CPU看似可以同时处理多件事 在微观上,一个CPU同一时刻只能处理一件事 结论:线程的执行具有随机性,我们控制不了,是由OS底层的算法来决定的
4.线程有几种状态?它们是怎么转换的?
-
新建状态:new–申请PCB,进行资源的分配
-
就绪/可运行状态:万事俱备只欠CPU,其实是将创建好的线程对象加入到就绪队列中,等待OS选中,这个选择我们是控制不了的
-
执行/运行状态:就绪队列中的线程被OS选中了,正在执行 注意:只有就绪状态才能切换成执行状态
-
阻塞状态:线程在执行中遇到了问题: 锁阻塞、休眠阻塞、等待阻塞…问题解决后再加入到就绪队列中
-
终止状态:线程成功执行完毕,释放资源
package cn.tedu.thread; /*本类用于实现多线程编程方案1:继承Thread类的方式*/ public class TestThread { public static void main(String[] args) { Thread t1 = new MyThread();/*对应的是线程的新建状态*/ Thread t2 = new MyThread(); Thread t3 = new MyThread(); Thread t4 = new MyThread(); // /*5.1测试1:自己主动调用run(),并没有多线程的效果*/ // t1.run(); // t2.run(); // t3.run(); // t4.run(); t1.start();/*对应的是线程的就绪状态*/ t2.start(); t3.start(); t4.start(); } } //1.自定义线程类,并且让这个类继承Thread类 class MyThread extends Thread{ //3.在run()完成自己的业务 //业务需求:打印10次当前正在干活的业务 @Override public void run() { for (int i = 0; i <10; i++) { System.out.println(i+getName()); } } }
package cn.tedu.thread; /*本类用于多线程编程实现方案二:实现Runnable接口*/ public class TestRunnable { public static void main(String[] args) { //4.创建自定义线程类对象 MyRunnable t = new MyRunnable(); //5.创建线程对象,并将业务对象交给线程对象 Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); //6.以多线程的方式启动线程 t1.start(); t2.start(); t3.start(); t4.start(); } } //1.自定义多线程类 class MyRunnable implements Runnable { //2.添加父接口中未实现的抽象方法,这个方法用来完成自己的业务 @Override public void run() { //3.完成业务:打印10次当前正在干活的线程名 for (int i = 0; i < 10; i++) { /*问题:自定义类与与父接口Runable中都没有获取名字的方法 * 所以还需要从Thread类里找 * currentThread():静态方法,获取当前正在执行的线程对象 * getName():获取当前正在执行的线程对象的名称*/ System.out.println(Thread.currentThread().getName() + i); } } }
同步:体现了排队的效果,同一时刻只能有一个线程独占资源,其他没有权利的线程排队。 坏处就是效率会降低,不过保证了安全。 异步:体现了多线程抢占资源的效果,线程间互相不等待,互相抢占资源。 坏处就是有安全隐患,效率要高一些。
synchronized同步关键字
写法
package cn.tedu.tickets; /*本类用于改造多线程售票案例,解决数据安全问题*/ public class TestRunnableV2 { public static void main(String[] args) { //5.创建目标业务类对象 TicketR2 target = new TicketR2(); //6.创建线程对象 Thread t1 = new Thread(target); Thread t2 = new Thread(target); Thread t3 = new Thread(target); Thread t4 = new Thread(target); //7.以多线程的方式运行 t1.start(); t2.start(); t3.start(); t4.start(); } } /*1.多线程中出现数据安全问题的原因:多线程程序+共享数据+多条语句操作共享数据*/ /*2.同步锁:相当于给容易出现问题的代码加了一把锁,包裹了所有可能会出现数据安全问题的代码 * 加锁之后,就有了同步(排队)的效果,但是加锁的话,需要考虑: * 锁的范围:不能太大,太大,干啥都得排队,也不能太小,太小,锁不住,还是会有安全隐患*/ //1.创建自定义多线程类 class TicketR2 implements Runnable { //3.定义成员变量,保存票数 int tickets = 100; //创建锁对象 Object o = new Object(); //2.实现接口中未实现的方法,run()中放着的是我们的业务 @Override public void run() { //4.通过循环结构完成业务 while (true) { /*3.同步代码块:synchronized(锁对象){会出现安全隐患的所有代码} * 同步代码块在同一时刻,同一资源只会被一个线程独享*/ /*这种写法不对,相当于每个线程进来的时候都会new一个锁对象,线程间使用的并不是同一把锁*/ //synchronized (new Object()){ //修改同步代码块的锁对象为成员变量o,因为锁对象必须唯一 synchronized (o) {//同步代码块解决的是重卖的问题 //如果票数>0就卖票 if (tickets > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //4.1打印当前正在售票的线程名以及票数-1 System.out.println(Thread.currentThread().getName() + "=" + tickets--); } //4.2退出死循环--没票的时候就结束 if (tickets <= 0) break; } } } }
package cn.tedu.tickets; /*本类用于改造多线程售票案例,解决数据安全问题*/ public class TestThreadV2 { public static void main(String[] args) { //5.创建多个线程对象并以多线程的方式运行 TickectT2 t1 = new TickectT2(); TickectT2 t2 = new TickectT2(); TickectT2 t3 = new TickectT2(); TickectT2 t4 = new TickectT2(); t1.start(); t2.start(); t3.start(); t4.start(); } } //1.自定义多线程类 class TickectT2 extends Thread { //3.新增成员变量用来保存票数 static int tickets = 100; //static Object o = new Object(); //2.添加重写的run()来完成业务 @Override public void run() { //3.创建循环结构用来卖票 while (true) { //Ctrl+Alt+L调整代码缩进 //7.添加同步代码块,解决数据安全问题 //synchronized (new Object()) { /*static的Object的对象o这种写法也可以*/ //synchronized (o) { /*我们每通过class关键字创建一个类,就会在工作空间中生成一个唯一对应的类名.class字节码文件 * 这个类名.class对应的对象我们称之为这个类的字节码对象 * 字节码对象极其重要,是反射技术的基石,字节码对象中包含了当前类所有的关键信息 * 所以,用这样一个唯一且明确的对象作为同步代码块的锁对象,再合适不过了*/ synchronized (TickectT2.class) {/*比较标准的写法*/ if(tickets > 0){ //6.添加线程休眠,暴露问题 try { Thread.sleep(10);//让线程休眠,增加线程状态切换的频率 } catch (InterruptedException e) { e.printStackTrace(); } //4.1打印当前正在售票的线程名与票数-1 System.out.println(getName() + "=" + tickets--); } //4.2给程序设置一个出口,没有票的时候就停止卖票 if (tickets <= 0) break; } } } }