Java 多线程基础

Java 多线程基础

线程

程序、进程和线程

程序:Program,是一个指令的集合

进程:Process,(正在执行中的程序)是一个静态的概念

进程是程序的一次静态态执行过程, 占用特定的地址空间.

每个进程都是独立的,由3部分组成cpu,data,code

缺点:内存的浪费, cpu的负担

线程:是进程中一个“单一的连续控制流程” (a singles Thread,equential flow of control)/执行路径

Threads run at the same time, independently of one another

一个进程可拥有多个并行的(concurrent)线程

一个进程中的线程共享相同的内存单元/内存地址空间 -> 可以访问相同的变量和对象,而且它们从同一堆中分配对象 -> 通信、 数据交换、同步操作

由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快。

Java虚拟机启动的时候会有一个进程java.exe,该进程中至少有一个线程,在负责java程序的执行。而且这个线程运行的代码存在于main方法中,该线程称之为主线程。

一个进程中的线程共享代码和数据空间

线程结束,进程未必结束,但进程结束,线程一定结束

进程中包含线程,线程是进程的一部分

区别进程线程
根本区别作为资源分配的单位调度和执行的单位
开销每个进程都有独立的代码和数据空间( 进程上下文),进程间的切换会有较大 的开销。线程可以看成时轻量级的进程,同一 类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC), 线程切换的开销小
所处环境在操作系统中能同时运行多个任务(程序)在同一应用程序中有多个顺序流同时执行
分配内存系统在运行的时候会为每个进程分配不同的内存区域除了CPU之外,不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源
包含关系没有线程的进程是可以被看作单线程 的,如果一个进程内拥有多个线程, 则执行过程不是一条线的,而是多条 线(线程)共同完成的。线程是进程的一部分,所以线程有的 时候被称为是轻权进程或者轻量级进程。

线程的创建和启动

在Java中负责线程的这个功能的是Java.lang.Thread 这个类

可以通过创建 Thread 的实例来创建新的线程。

每个线程都是通过某个特定Thread对象所对应的方法run( )来完成其操作的,方法run( )称为线程体。

通过调用Thead类的start()方法来启动一个线程。

1、继承Thread类

继承Thread类

重写run方法

创建对象,调用start()方法启动线程

MyThread mt = new MyThread();

mt.start();

2、实现Runnable接口 (Callable接口及Future)

实现Runnable接口

重写run方法

创建对象,调用start()方法启动线程

Thread t = new Thread(new MyRunnable());

t.start();

小结

继承Thread类方式的缺点:那就是如果我们的类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承 Thread类

通过Runnable接口实现多线程

优点:可以同时实现继承。实现Runnable接口方式要通用一些。
1)避免单继承
2)方便共享资源 同一份资源 多个代理访问

线程的生命周期

  • 新生状态
    • 用new关键字建立一个线程后,该线程对象就处于新生状态。
    • 处于新生状态的线程有自己的内存空间,通过调用start()方法进入就绪状态。
  • 就绪状态
    • 处于就绪状态线程具备了运行条件,但还没分配到CPU,处于线程就绪队列,等待系统为其分配CPU。
    • 当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,该动作称为“CPU调度”。
  • 运行状态
    • 在运行状态的线程执行自己的run方法中代码,直到等待某资源而阻塞或完成任何而死亡。
    • 如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态。
  • 阻塞状态
    • 处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己运行,进入阻塞状态。
    • 在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续执行。
  • 死亡状态
    • 死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有三个,一个是正常运行的线程完成了它的全部工作;另一个是线程被强制性地终止,如通过stop方法来终止一个线程【不推荐使用】 ;三是线程抛出未捕获的异常。

在这里插入图片描述

线程操作的相关方法

方法名称描述
public static Thread currentThread()返回目前正在执行的线程
public final String getName()返回线程的名称
public final int getPriority()返回线程的优先级
public final void setPriority(String name)设定线程优先级
public final boolean isAlive()判断线程是否在活动,如果是,返回true,否则返 回false
public final void join()调用该方法的线程强制执行,其它线程处于阻塞状态,该线程执行完毕后,其它线程再执行
public static void sleep(long millis)使用当前正在执行的线程休眠millis秒,线程处于阻塞状态
public static void yield()当前正在执行的线程暂停一次,允许其他线程执行,不阻塞,线程进入就绪状态,如果没有其他等待执行的线程,这个时候当前线程就会马上恢复执行
public final void stop()强迫线程停止执行。已过时。不推荐使用。

阻塞状态

有三种方法可以暂停Thread执行:

  1. sleep:
    不会释放锁, Sleep时别的线程也不可以访问锁定对象。
  2. yield:
    让出CPU的使用权,从运行态直接进入就绪态。让CPU重新挑选哪一个线程进入运行状态。
  3. join:
    当某个线程等待另一个线程执行结束后,才继续执行时,使调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行

线程同步

安全性问题

在这里插入图片描述

同步

同步的前提:

必须有两个或两个以上的线程

必须是多个线程使用同一资源

必须保证同步中只能有一个线程在运行

同步代码块

public void run() {
	while(true){
		synchronized (this) {//通常将当前对象作为同步对象
			//代码块
		}
	}
}

同步方法

public void run() {
	while(true){
        foo();
	}
}
public synchronized void foo(){
//通常将当前对象作为同步对象
	if ( ... ) {
		Thread.sleep(10);
		...
	}
}

小结:

同步监视器

– synchronized(obj){}中的obj称为同步监视器

– 同步代码块中同步监视器可以是任何对象,但是推荐使用共享资源作为同步监视器

– 同步方法中无需指定同步监视器,因为同步方法的监视器是this,也就是该对象本身

同步监视器的执行过程

– 第一个线程访问,锁定同步监视器,执行其中代码

– 第二个线程访问,发现同步监视器被锁定,无法访问

– 第一个线程访问完毕,解锁同步监视器

– 第二个线程访问,发现同步监视器未锁,锁定并访问

死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

出现死锁条件:

  • 互斥条件

    • 即某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程占有。
  • 不剥夺条件

    • 进程所获得的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,而只能由该资源的占有者进程自行释放。
  • 请求和保持条件

    • 进程至少已经占有一个资源,但又申请新的资源;由于该资源已被另外进程占有,此时该进程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源。
  • 环路条件

线程间通信

Java提供了3个方法解决线程之间的通信问题

方法名作用
final void wait()表示线程一直等待,直到其它线程通知
final void wait(long timeout)线程等待指定毫秒参数的时间
final void wait(long timeout,int nanos)线程等待指定毫秒、微妙的时间
final void notify()唤醒一个处于等待状态的线程
final void notifyAll()唤醒同一个对象上所有调用wait()方法的线程, 优先级别高的线程优先运行

注意事项: 以上方法都只能在同步方法或者同步代码块中使用,否则会抛出异常

生产者与消费者问题

未完待续

线程池

优点:

1、使用线程池可以重复利用已有的线程继续执行任务,避免线程在创建和销毁时造成的消耗

2、由于没有线程创建和销毁时的消耗,可以提高系统响应速度

3、通过线程可以对线程进行合理的管理,根据系统的承受能力调整可运行线程数量的大小等

线程池执行所提交的任务过程:
1、先判断线程池中核心线程池所有的线程是否都在执行任务。如果不是,则新创建一个线程执行刚提交的任务,否则,核心线程池中所有的线程都在执行任务,则进入第2步;

2、判断当前阻塞队列是否已满,如果未满,则将提交的任务放置在阻塞队列中;否则,则进入第3步;

3、判断线程池中所有的线程是否都在执行任务,如果没有,则创建一个新的线程来执行任务,否则,则交给饱和策略进行处理

线程池分类

在这里插入图片描述

生命周期

RUNNING : 能接受新提交的任务,并且也能处理阻塞队列中的任务;
SHUTDOWN: 关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务。
STOP: 不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程。
TIDYING: 如果所有的任务都已终止了, workerCount (有效线程数) 为0,线程池进入该状态后会调用terminated() 方法进入TERMINATED 状态。
TERMINATED: 在terminated() 方法执行完后进入该状态,默认terminated()方法中什么也没有做

在这里插入图片描述

创建及参数意义

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExcutionHandler handler){
    
}

corePoolSize:核心线程池的大小

maximumPoolSize:线程池能创建线程的最大个数

keepAliveTime:空闲线程存活时间

unit:时间单位,为keepAliveTime指定时间单位

workQueue:阻塞队列,用于保存任务的阻塞队列

threadFactory:创建线程的工程类

handler:饱和策略(拒绝策略)

阻塞队列:

▪ ArrayBlockingQueue
▪ LinkedBlockingQueue
▪ DelayQueue
▪ PriorityBlockingQueue
▪ SynchronousQueue

拒绝策略

▪ ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
▪ ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
▪ ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
▪ ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

execute方法执行逻辑

在这里插入图片描述

如果当前运行的线程少于corePoolSize,则会创建新的线程来执行新的任务;

如果运行的线程个数等于或者大于corePoolSize,则会将提交的任务存放到阻塞队列workQueue中;

如果当前workQueue队列已满的话,则会创建新的线程来执行任务;

如果线程个数已经超过了maximumPoolSize,则会使用饱和策略RejectedExecutionHandler来进行处理

线程池关闭

关闭线程池,可以通过shutdown和shutdownNow两个方法

原理:遍历线程池中的所有线程,然后依次中断

1、 shutdownNow首先将线程池的状态设置为STOP,然后尝试停止所有的正在执行和未执行任务的线程,并返回等待执行任务的列表;

2、 shutdown只是将线程池的状态设置为SHUTDOWN状态,然后中断所有没有正在执行任务的线程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值