文章目录
线程相关概念:
进程基本介绍
1.进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。
2.进程是程序第一次执行过程,或是正在运行的一个程序,是动态过程:有它自身的产生,存在和消亡的过程
单线程与多线程
1.单线程:同一个时刻,只允许执行一个线程
2.多线程:同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件
并发与并行
1.并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的来说,单核cup实现的多任务就是并发
2.并行:同一个时刻,多个任务同时执行,多核cup可以实现并行,并发和并行
说明:当main函数启动一个线程时,main函数不会阻塞,会继续执行
获取本电脑cup几个核的方法:
public static void main(String[] args) {
Runtime runtime=Runtime.getRuntime();
int cpuNums=runtime.availableProcessors();
System.out.println(cpuNums);
}
线程常用方法:
1.setName// 设置线程名称,使之与参数name相同
2.getName//返回该线程的名称
3start //使该线程开始执行:java虚拟机底层调用该线程的start0方法
4.run //调用线程对象run方法;
5.setPriority //更改线程的优先级
6.getPriotity //获取线程的优先级
7.sleep //在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
8.interrupt //中断线程
9.yieId:线程的礼让。让出CPU,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
10.join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
创建线程的两种方法
在java中线程来使用有两种方法。
1.继承Thread类,重写run方法
public class Thread_{
public static void main(String[] args) {
//创建一个继承了线程类的对象
Thr thr=new Thr();
//开启线程
thr.start();
}
}
class Thr extends Thread{
//重写了Thread类的run方法
public void run() {
int a=0;
while(true) {
try {
//休眠5毫秒
Thread.sleep(500);
}catch(Exception e) {
e.printStackTrace();
}
System.out.println(a++);
if(a>5) break;
}
}
}
2.实现Runnable接口,重写run方法
public class Thread_{
public static void main(String[] args) {
Thr thr=new Thr();
//匿名对象,向Thread构造中传入了一个实现了Runnable的类对象
new Thread(thr).start();
}
}
//实现了Runnable接口
class Thr implements Runnable{
//重写了Runnable里的run()方法
public void run() {
int a=0;
while(true) {
try {
Thread.sleep(500);
}catch(Exception e) {
e.printStackTrace();
}
System.out.println(a++);
if(a>5) break;
}
}
}
说明:
1).java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能了
2).java设计者们提供了另一个方式创建线程,就时通过实现Runnable接口来创建线程
start()函数开启线程过程:
start0() 是本地方法,是JVM调用,底层是c/c++实现;
真正实现多线程的效果,是statr0,而不是run
start() 方法调用start0() 方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态,具体什么时候执行,取决于CPU,由CPU统一调度
用户线程和守护线程
setDaemon(true);
1.用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
2.守护线程:一般时为工作线程服务的,当所有的用户线程结束,守护线程自动结束,垃圾回收机制就时一个守护线程
线程的生命周期:
线程同步机制:
1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
2.也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。
同步具体方法
1.同步代码块
synchronized(对象){//得到对象的锁,才能操作同步代码
//需要被同步代码;
}
2.synchronized还可以放在声明中,表示整个方法-为同步方法
public synchronized void m(String name){
//需要被同步的代码
}
3.如何理解:
就好想,某个人上厕所先把门关上(上锁),完事后在出来(解锁),那么其它人就可在使用厕所了
互斥锁
基本介绍:
1.java在java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
2.每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
3.关键字synchronized 来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
4.同步的局限性:导致程序的执行效率要降低
5.同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
6.同步方法(静态的)的锁为当前类本事。
注意事项和细节
1.同步方法如果没有使用static修饰:默认锁对象为this
2.如果方法使用static修饰,默认锁对象:当前类.class
3.实现的落地步骤:
- 需要先分析上锁的代码
- 选择同步代码块或同步方法
- 要求多个线程的锁对象为同一个即可
线程调度
线程有两种调度模型
- 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
- 抢占是调度模型:优先让优先级高的线程使用cpu,如果线程的优先级相同,那么就会随机选择一个,优先级高的线程获取cpu时间片相对多一些
java使用的是抢占式调度模型
假如计算机只有一个cpu,那么在某一个时刻只能执行一条命令,线程只有得到cpu的时间片,也就是使用权,才可以执行指令,所以说多线程程序的执行是有随机性,因为谁抢到cpu的使用权是不一定的
Thread类中设置了获取线程优先级的方法
-
public final int getPiority(): 返回此线程的优先级
-
public final void setPiority(1~10): 更改线程的优先级
线程默认优先级是5;线程优先级的范围是:10
线程优先级高仅仅表示线程获取的cpu时间片的几率高,但是在次数比较多,或者多次运行的时候才能看到你想要的效果
线程控制
方法名 | 说明 |
---|---|
static void sleep(long 毫米数) | 使用当前正在执行的线程停留(暂停执行)指定的毫秒数 |
void join() | 等待这个线程死亡 |
void setDaemon(boolean on) | 将线程标记位守护线程,当运行的线程都是守护线程时,java虚拟机将退出 |
sleep() : 运行到sleep()方法时会暂停指定的毫秒数之后在运行
join():等待指定的线程执行结束才开始执行其他的线程;
Thread.currentThreod().setName();设置主线程
void setDaemon(): 将此线程标记守护线程,当运行的线程都是守护线程时,java虚拟机将退出
多线程的实现方式
1.直接继承Tread类
2.实现Runnable接口
- 定义一个类实现Runnble接口
- 在类中重写run()方法
- 创建类的对象
- 创建Thread类的对象,把自定义类对象作为构造方法的参数
- 启动线程
相比继承Thread类,实现runnable接口的好处
避免了java单继承的局限性
适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码,数据有效的分离,较好的体现了面向对象的设计思想
线程同步代码块
锁多条语句操作共享数据,可以使用同步戴帽块实现
格式:
synchronized(任意对象){
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁,任意对象就可以看成是一把锁
同步方法
同步方法:就是把synchronized关键字加到方法上
-
格式:
修饰符synchronized返回值类型方法名(方法参数){ }
同步方法的锁对象是什么呢?
- this
线程安全的类
stringBuffer
线程安全 可变的字符序列
从版本JDK5开始,被StringBuilder替代,通常应用该使用StringBuilder类,因为他支持所有相同的操作,但它更快,因为他不执行同步
Vector
从java2平台v1.2开始,该类改进了List接口,使其成为java Collections Framework的成员。与新的集合实现不同,Vector被同步,如果,不需要线程安全的实现,建议使用ArrayList代替Vector
Hashtable
该类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键值或者值
从java 2平台v1.2开始,该类进行了改进,实现了Mao接口,使其成为Java Collections Framework的成员,与新的集合实现不同,Hashtable被同步,如果不需要线程安全的实现,建议使用Hash
List=Collections.synchronizedList(new ArrayList);使用Collections.synchronizedList()方法可以使list变为线程安全的
Lock锁
我们可以理解同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供了比synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁方法
- void Lock();获得锁
- void unlock();释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化ReentrantLock的构造方法
ReentrantLock() : 创建一个ReentrantLock的实例
生产者消费者模式概述
为了体现生产和消费过程中的等待和唤醒,java就提供了一个方法供我们使用,这几个 方法在Object类中Object类的等待和唤醒方法:
方法名 | 说明 |
---|---|
void wait() | 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法 |
void notify() | 唤醒正在等待对象监视器的单个线程 |
void notifyAll() | 唤醒正在等待对象监视器的所有程序 |