什么是多线程
多线程是指从软件或者硬件上实现线程并发的技术
线程和进程
总:按照计算机组成原理书上面说的,线程是由一个程序控制块(pcb),相关代码段和相关资源组成的,之所以出现线程这个概念是因为一个进程在系统当中“太重了”,如果频繁的使用进程会加重系统的运行负担,而引入线程能较大程度上解决这个问题。一般来说一个进程包含多个线程,一个线程是程序的一条执行路线。
分:进程:是正在运行的软件
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
动态性:进程的实质是程序的一次执行过程,进行是动态产生,动态消亡的
并发性:任何进程都可以同其他进程一起并发执行
线程:是进程的单个顺序控制流,是一条执行路径
多线程:一个进程有多条执行路径,则称为多线程
不得不提到的并发和并行
简单提一下,并发是指在同一时间间隔里有多个线程在运行,就好像是你一边玩电脑,一般玩手机,一边喝饮料,虽然是单独进行的,但是这几个动作都是在某一时间间隔里发生的。
并行指的是线程同时运行,就像你又喝饮料又敲键盘这两个动作同时进行,所以一般支持并行的电脑都是多核的,单核的电脑是不支持并行的。
线程的生命周期
在计算机操作系统中线程的状态包括1 创建 2就绪 3运行 4阻塞 5 终止
1 创建,系统为进程分配资源
2 就绪,进程等待运行
3 运行,进程执行
4 阻塞,等待接受系统发来的数据而进入阻塞状态
5 终止,线程死亡,系统释放线程的资源
在Java中线程的三种创建方式
1 通过继承Thread类来实现
2 通过实行Runnable接口来实现
3 上面创建线程的方法耦合度太高了,通过实现Callable接口重写里面的call方法,在通过把创建的这个实现类当做参数传递给FutureTask类来创建对象,最后把FutureTask类当做参数传递给Thread类。
关于线程中的run方法
1 为什么要重写run()方法、
因为run()是用来封装被线程执行的代码的,线程启动会执行里面的代码
2 run()方法和start()方法的区别
run():封装线程执行的代码,直接调用就相当于普通方法的调用,并没有开启线程
start():启动线程,然后又jvm调用线程的run()方法
关于同步的问题
线程是异步的运行,这样在访问公共资源就会出现许多的问题,就好像你不好好排队乱插队一样,你付了钱,东西却被插队的拿走了,这你能忍?在线程中也是一样,所以我们可以通过同步代码块来解决这个问题,同步代码块格式为synchronized(任意对象){ 语句操作共享数据的代码 },就相当于上了一个锁,你有这个锁,你进屋子里锁上门,然后买东西,付完钱拿完东西再开锁,这个过程别人是进不来的。而且指定的锁对象必须是唯一的,因为如果锁对象不唯一,其它线程还是能打开“门”进入程序操作共享资源的。
关于synchronized
好处是解决了多线程的数据安全问题,坏处是当线程很多时,每个线程都去判断这个锁是不是锁上的,资源无形当中就消耗了许多资源,所以synchronized是悲观锁。syunchronized也可以加到方法上,写在返回值的前面,表示同步方法,它的锁对象就是this,也就是本类,而同步静态方法的对象是字节码文件,也就是类名.class
新的锁
jdk5后增加了Lock锁,因为syunchronized锁不能看见在在哪里加上了锁,在哪里释放了锁,为了更加清晰表达如何加锁和释放锁,jdk5后提供了新的锁对象Lock,Lock不能直接实例化,要采用它的实现类ReentrantLock来实例化,而且解锁的代码必须要放在finally当中,无论程序能不能全部运行,都必须释放锁。
线程唤醒和等待
因为唤醒和等待是方法是写在object类当中的,而object类是所有类的基类,所有很多类都可以调用这个方法,同样唤醒和等待方法要写在while循环里面,因为线程有一个虚假唤醒的姿态,就像没起床但是却说起床了一样,所以要用while持续判断。