java中程序进程、线程、多线程,线程同步、synchronized、Lock锁总结

一、进程&线程

  1. 程序
    是为完成特定的任务或需求,而用某种语言编写的一组指令的集合
  2. 进程
    是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
  3. 线程
    进程的一个执行路径,共享一块内存,进程至少有一个线程
    并发执行,是进程的进一步划分
  4. 线程调度
    分时调度
    轮流使用cpu,平均分配cpu使用率
    抢占式调度
    让优先级更高的先使用cpu,优先级相同的随机分配
    java默认抢占式调度
    java的调度方法
    同优先级线程组成先进先出队列(先到先服务),使用时间片策略
    对高优先级,使用优先调度的抢占式策略
  5. 线程优先级
    MAX_PRIORITY:10
    NORM_PRIORITY:5
    MIN_PRIORITY:1
  • 方法
    getPriority|返回此线程的优先级|
    setPriority(int newPriority)|更改此线程的优先级,默认5,1~10
    etDaemon|守护线程,全为守护线程JVM退出|

对于CPU的一个核来说,同一时刻只能运行一个线程,多个线程轮流切换非常快。我们看做是同一时刻多个任务执行
多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使用率更高。

  1. 线程分类
    守护线程
    用来服务用户线程的
    通过在start方法前调用thread.setDaemon(true)可以把一个用户线程变成守护线程
    若JVM都是守护线程,当前JVM将退出
    JVM垃圾回收就是典型的守护线程
    用户线程

  2. 同步&异步

     同步 			排队执行,效率低 		
     异步 			同时执行,效率高但数据不安全
    
  3. 并发&并行

     并发  
          指两个或多个事件在一个时间段内的发生
     并行
     	  指两个或多个事件在同一时刻执行
    

二、线程的创建方式

1.Thread类
概述
  • java.lang.Thread
  • 每个线程都是通过Thread对象的run()方法完成操作,run()方法体称为:线程体
  • 通过该Thread对象的start()方法来启动这个线程,而非直接调用run方法。
  • 一个线程对象只能调用一次start,如果重复调用,抛出lllegalThreadStateException异常
构造
  • Thread():创建新的对象
  • Thread(String threadName):创建线程并指定线程实例名
  • Thread(Runnable target):指定线程的目标对象,它实现了runnable接口的run方法
  • Thread(Runnable target,String name):创建新的Thread对象
方法
  1. start
    启动线程
  2. getName
    获得线程名
  3. setName
    设置线程名
  4. currentThread
    返回当前线程
  5. run
    真正的线程任务的方法
  6. setDaemon()
    参数为true,设置该线程为守护线程
    必须在线程启动之前,否则报IllegalThreadStateException
  7. yield
    线程让步
    暂停当前正在执行的线程,把机会让给优先级相同或者更高的线程
    若队列中没有同优先级的线程,忽略此方法
  8. join
    线程阻塞,加入线程运行
    当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完为止
    低优先级的线程也可以获得执行
  9. sleep
    线程休眠
    令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队
    抛出InterruptedException异常
  10. isAlive
    判断线程是否存活
  11. interrupt
    中断线程
  12. interrupted
    测试当前线程是否已被中断
创建线程方式

创建 继承Thread类的类的对象,该类重写run方法

	A a = new A();
	a.start();
2.Runnable接口
创建方式

类实现Runnable接口,重写run方法,创建该类得到对象任务,new Thread(类对象)来得到线程对象

好处
  • 通过创建任务,然后线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况
  • 可以避免单继承所带来的局限
  • 任务与线程本身是分离的,提高了程序的健壮性
  • 后续学习的线程池技术,接收Runnable类型的任务,不接受Thread类的线程
3.Callable接口
创建方式
  1. 编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> {
	@Override
	public <T> call() throws Exception {
	return T;
	}
}
  1. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
  1. 通过Thread,启动线程
new Thread(future).start();
FutureTask对象
get
	如果需要等待计算完成,然后检索其结果
get​(long timeout, TimeUnit unit) 
	如果需要,最多等待计算完成的给定时间,然后检索其结果(如果可用)。  
	TimeUnit定义单位
isDone
	判断子线程是否完成
cancel
	传入true表示取消该方法,返回值也是boolean,返回false表示该任务已经完毕,无法取消了,返回true就是取消成功
4.Runnable与Callable

接口定义

接口定义
//Callable接口
public interface Callable<V> {
	V call() throws Exception;
}
//Runnable接口
public interface Runnable {
	public abstract void run();
}

相同点

都是接口
都可以编写多线程程序
都采用Thread.start()启动线程

不同点

Runnable没有返回值;Callable可以返回执行结果
Callable接口的call()允许抛出异常;Runnable的run()不能抛出

获取返回值

Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,
此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
5.线程池

  Executors一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作

三、线程安全&同步

1.同步
同步锁机制
	在《Thinking in java》中,是这么说的:对于并发工作,
你需要某种方式来防止两个任务访问相同的资源(其实就是共享资源竞争)。
防止这种冲突做法:当资源被一个任务使用时,在其上加锁。第一个访问
某项资源的任务必须锁定这项资源,使其他任务在其解锁之前,就无法访问它了
,而在其被解锁之时,另一个任务就可以锁定并使用它了。
注意
	必须确保使用同一资源的多个线程共用一把锁,非常重要。否则无法保证共享资源
的安全
	一个线程类中所有静态方法共用同一把锁(类名.class),所有非静态方法共用
同一把锁(this),同步代码块(指定需谨慎)
同步的范围
1.明确哪些是多线程执行的代码
2.多个线程是否有共享的数据
3.明确多个线程运行代码中是否有多条语句操作共享数据

所有操作共享数据的这些语句都要放在同步范围中

释放锁操作
1.当前线程的同步方法、同步代码块执行结束
2.线程在执行中遇到break、return代码
3.出现未处理的Error、Exception,导致异常结束
4.执行了当前线程对象的wait方法,当前线程暂停,释放锁
join也会释放锁资源
不会释放锁操作
1.线程执行同步代码块或同步方法时,调用Thread.sleep()\Thread.yield()方法暂停当前线程执行
	yeild()方法不释放锁资源的原因。 yeild()方法是暂停当前线程,让当前线程回到了可执行状态,但是不一定释放锁资源(可能释放锁资源,也可能不会释放锁资源)。被yeild()暂停的线程可能会回到可执行状态之后立即执行。因此可能不会释放锁资源。
2.线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)

应尽量避免使用suspend和resume来控制线程

2.synchronized
  • 任何对象 都可作为锁对象,同一任务含有同一锁对象才会保证线程同步
    同步代码块
  • synchronized(obj){ }
  • 锁自己指定,可以使this或者className.class
    同步方法
  • public synchronized void test(){ }
  • 静态方法的锁是 className.class
  • 非静态方法锁是this
3.死锁
不同线程分别占用对方需要同步的资源,不放弃,都在等待对方让步,形成线程死锁
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法
继续

解决

  • 专门的算法、原则
  • 尽量减少同步资源的定义
  • 尽量避免嵌套同步
4.Lock锁

JDK5.0开始,加入通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当

  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
  • 锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
  • ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义。比较常用ReentrantLock,可以显示加锁,释放锁
  • 格式
 		//加锁
        lock.lock();
        try{
            //代码
        }finally {
            //释放锁
            lock.unlock();
        }
5.synchronized与Lock锁比较
  • Lock是显示锁(手动开启和释放锁),synchronized是隐式锁,出了作用域自动释放
  • Lock只有代码块锁,synchronized有代码块和方法锁
  • 使用Lock锁,JVM将花费较少时间来调度来调度线程,性能更好。并具有良好扩展性(提供更多的子类)
  • 优先使用顺序
    同步代码块(已经进入方法体,分配了相应资源)->同步方法(在方法体外)
6.线程通信
  • wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程等待其他线程调用notify和notifyAll方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行
  • notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待。
  • noyifyAll():唤醒正在排队等候资源的所有线程结束等待
    注意
    这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则报java.lang.lllegalMonitorstateException异常
    因为这三个方法必须有锁对象调用,而任意对象都可作为synchronized的同步锁,因此这三个方法只能声明在Object类中

四、线程池

1.概述
  • 背景
      如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间
  • Executors一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作
2.好处
  • 降低资源消耗。
  • 提高响应速度。
  • 提高线程的可管理性
3.四种线程池
(1)缓存线程池:newCachedThreadPool
  • ExecutorService service = Executors.newCachedThreadPool();
  • (长度无限制)
  • 过程:
    1. 判断线程池是否存在空闲线程
    2. 存在则使用
    3. 不存在,则创建线程 并放入线程池, 然后使用
(2)定长线程池:newFixedThreadPoll
  • ExecutorService service = Executors.newFixedThreadPool()
  • (长度固定)
  • 过程:
    1. 判断线程池是否存在空闲线程
    2. 存在则使用
    3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
  1. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
(3)单线程线程池:newSingleThreadExecutor
  • ExecutorService service = Executors.newSingleThreadExecutor()
  • 1个
  • 过程:
    1. 判断线程池 的那个线程 是否空闲
    2. 空闲则使用
    3. 不空闲,则等待 池中的单个线程空闲后 使用
(4)周期性任务定长线程池:newScheduledThreadPool
  • ScheduledExecutorService service = Executors.newScheduledThreadPool(corePoolSize);
  • 周期任务 定长线程池
  • 过程
    1. 判断线程池是否存在空闲线程
    2. 存在则使用
    3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
    4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
  • 周期性任务执行
    • 定时任务
      schedule(Callable callable,long delay, TimeUnit unit);
        参数1command. runnable类型的任务
        参数2delay. 时长数字
        参数3unit. 时长数字的单位
    • 周期性任务
      scheduleAtFixedRate(Runnable command,long initialDelay, long period, TimeUnit unit);
        参数1command. runnable类型的任务
        参数2initialDelay. 时长数字(延迟执行的时长)
        参数3period. 周期时长(每次执行的间隔时间)
        参数4unit. 时长数字的单位
  • 注意生成对象为ScheduledExecutorService
4.常用参数
  • corePoolSize:核心池的大小、
  • maximumPoolSize:最大线程池数
  • keepAliveTime:无任务时线程最多保持时间会终止
5.相关API

ExecutorService和Excutors

ExecutorService: 真正线程池接口。常见子类:ThreadPoolEcecutor
Excutors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池
有四种线程池:newFixedThreadPool、newCachedThreadPool、newScheduledThreadPool、newSingleThreadPool

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Itfuture03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值