一.并行和并发
并行:指两个或多个事件在同一时刻点发生
并发:指两个或多个事件在同一时间段内发生
注释:
时间片是CPU分配各个程序的运行时间(很小的概念)
在操作系统中,并发性指的是在一段时间内宏观上有多个程序(进程)在同时运行,,但是在单CPU系统中,每个时刻只能有一道程序执行(时间片),所以在微观上这些程序只能是分时交替执行,如果计算机系统有多个CPU(多核)的话,则这些并发执行的程序可以分配给多个CPU处理器上,实现多任务并行执行
单核处理器的计算机肯定是不能并行处理多个任务的,只能是多个任务在单个CPU上并发执行,同理,线程也是一样的,从宏观上考虑线程是并行执行的,但是从微观角度上看却是串行运行的
二.进程和线程
进程:是指一个内存中运行的应用程序,,每个进程都有自己独立的一块内存空间 ,,,,,一个应用程序上可以同时启动多个进程,例如:在360搜索中打开多个网页(观察任务管理器),每个360搜索.exe就是一个进程
线程:是指进程中的一个执行任务(控制单元),一个进程可以同时并发运行多个线程
注意:
1). 一个进程最少有一个线程 2). java程序的进程至少包括 主线程和垃圾回收线程
多进程:在操作系统中同时运行多个任务
多线程:在同一个进程中同时运行多个任务
进程与线程的区别:
进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程
线程:堆空间是共享的(new 对象()),栈空间是独立的,线程消耗的资源也比进程小,相互是可以影响的,又称之为轻型进程或者进程元 //把线程看做是一个对象
注释:
因为一个进程中的多个线程是并发运行的,那么从微观上看的话,执行也是有先后顺序的,那么那个线程先执行完全取决于CPU调度器(JVM),程序员是无法控制的
线程的调度:
计算机通常只有一个CPU时,在任意时刻只能执行一条计算机指令,每一个进程只有获得了CPU的使用权才能执行指令,那么,在运行池中,会有多个线程处于就绪状态等待JVM的调度
JVM采用的是抢占式调度,没有采用分时调度,因此可能出现多线程执行结果的随机性
多线程的优势:
多线程作为一种多任务,并发的工作方式,具备以下优势:
1).进程之间不能共享内存,而多线程可以共享堆内存空间
2).线程消耗的资源比进程小,任务并发时,多线程的执行效率高
3).java语言本身内置多线程的功能的支持而不是单纯的作为底层系统的调度方式,从而简化了多线程编程
多线程下载: 可以理解为一个线程就是一个文件的下载通道,多线程也就是开启了好几个下载通道,当服务器提供下载服务时,下载者是共享宽带的,在优先级相等的情况下,总服务器会对总下载线程进行平均分配字节,即每个线程下载速度相同,如果线程越多,则下载的越快(在下载中,每个线程就相当于一个通道,通道越多,执行的时间越短,下载的越快)
//多线程不是为了提供程序运行效率,而是通过提高资源使用效率来提高系统的效率
拓展:
市场卖的xx兆的宽带是以位(bit)计算的,而下载速度是以字节(Byte)计算的,一个字节占八个位
例如:1024kb/s的宽带是代表带宽是1024kb,而下载速度是带宽/8,即1024kb/8=128kb/s,所以下载的最大速度为128kb/s
三.创建和启动线程的方式
方式1:继承Thread类
方式2:实现Runnable类
注意:
线程类(java.lang.Thread):Thread类和其子类才能称之为线程类,阅读API
java中main方法的运行也属于主线程
方式1:继承Thread类
Tread的构造器
public Thread(Runnable target,String name)
public Thread(String name)
public Thread(Runnable target)
步骤:
1):定义一个类A继承于java.lang.Thread类
2):在A类中覆盖Thread类中的run方法
3):在run方法中编写我们要执行的操作,run方法里面是线程执行体
4):在main方法(线程)中,创建线程对象,并启动线程
创建线程类: A类 a=new A类();
调用线程对象的start方法: a.start(); //启动线程
注意:
千万不要调用run方法,如果调用run方法好比是调用对象方法,依然还是只有一个线程,并没有起到启动新线程的作用
实践如图:
方式2:实现Runnable接口
步骤:
1):定义一个类A实现与java.lang.Runnable接口,注意A类不是线程类
2):在A类中覆盖Runnable接口中的run方法
3):在run方法中写要执行的操作,run方法里面是新线程的线程执行体
4):在main方法(线程)中,创建线程对象,并启动线程
创建线程类对象: Thread t=new Tread(new A());
调用线程对象的start方法: t.start();
实践如图:
方式3:匿名内部类
四.多线程中继承方式和实现方式的区别
继承方式:
1):java中类是单继承的,如果继承了Thread了,就不能再继承其他直接父类
2):从操作上分析,继承方式更简单,获取线程名字也简单(操作上更简单)
3):从多线程共享同一个资源上分析,继承方式不能做到
例如下图: 小A,小B,小C三个人各吃了50个苹果,三个人一共吃了150个苹果,显然有些不合理
实现方式:
1):java中类可以实现多接口,此时该类还可以继承其他类,并且还可以实现多接口(设计上更优雅)
2):从操作上分析,实现方式稍微复杂点获取线程名字的方式也复杂点,得使用Thread.currentThread()来获取当前线程对象的引用,再调用getName()方法
3):从多线程共享一个资源上分析,实现方式可以做到
例如下图:小A, 小B, 小c三个人一共吃了50个苹果,能够实现预期的要求
五.线程的不安全问题分析
当多线程并发访问同一个资源对象(实现方式)时,可能出现线程不安全问题,但是观察打印的结果却没有发现什么问题,但是看不到问题不代表没有问题,只是因为自己的经验不足,问题出现的也不明显
为了让问题更加明显,引入Thread.sleep(int num)方法
Thread.sleep(10) ;//让当前线程睡10毫秒,出现的问题是当这个线程休息的时候,其他的线程会去抢夺资源,,,,,,,,, 可以用该方法来模拟网络延迟
举例如图:
如何解决线程并发访问同一个资源的不安全问题?有三种方式
* 解决方案:保证打印苹果编号和苹果数目减一是同步进行的* A线程进入操作的时候,线程B,C必须在外面等着,A操作结束后,B,C才有机会进入代码去执行
*
* 方式1.同步代码块
语法格式:
synchronized(同步锁) //一般为this
{
需要同步操作的代码
}
* 方式2.同步方法
方法的调用,在被调用的方法前加synchronized修饰,方法体中写需要同步操作的代码
* 方式3.锁机制(lock) //单例模式
语法格式:
class x {
private final ReentrantLock lock=new ReentrantLock()
//......
public void m() {
lock.lock();
try {
//......method body
}finally{
lock.unlock();
}
}
}
六.synchronized的好与坏
好处:保证了多线程并发访问时的同步操作,避免线程的安全性问题
缺点:使用synchronized的方法/代码块的性能比不用更低一些
建议:尽量减小synchronized的作用域
回顾内容:
Stringbuffer和StringBuilder的区别
StringBuffer和StringBuilder都是一个可变的字符序列,提供一个缓冲区,(两者都看作容器)
StringBuffer:线程安全,同步的,执行效率低
eg: public synchronized StringBuffer append(Objict obj) //方法有 synchronized 修饰,线程安全,同步,耗性能
StringBuilder:线程不安全,不同步的,执行效率高,并且单线程中优先采用StringBulidereg: public StringBuilder append(Object obj)