线程基础讲解

本文详细介绍了Java中的线程相关概念,包括进程、单线程与多线程、并发与并行的区别。讲解了线程的创建、线程的生命周期、线程同步机制如synchronized和Lock,以及线程调度和线程安全的类。同时提到了守护线程、线程控制方法如sleep和join,并介绍了生产者消费者模式的基本原理。
摘要由CSDN通过智能技术生成

线程相关概念:
进程基本介绍

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()唤醒正在等待对象监视器的所有程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值