java线程相关问题和各种锁

JAVA中线程相关问题

  1.线程基础

进程:进程是指正在进行的程序。当程序进入内存中执行时就变成了进程,具有一定的独立性。

线程:线程是进程中的执行单位,负责进程的执行。一个进程中有多个线程,多个线程在内存中通过分时调度和抢占式调度来执行。

单线程:又称为同步线程(与线程同步不同,线程同步指保证线程安全),代码自上至下执行。

多线程:又称为异步线程,多线程具有三大特性:原子性、可见性、有序性。

原子性:同一条线程中的数据处理,要么都进行,要么都不执行,要保证线程执行过程中所有数据保持一致。

可见性:一条线程修改共享变量值,其他线程立即得知这个修改后的值。(见第4点多线程第一段详解)

有序性:一条线程中执行顺序按照代码自上而下。

关于线程有许多知识点,如线程状态、线程池、多线程的安全问题与并发以及锁对象。

创建线程的几种方法:1.继承Thread,重写run()方法

2.实现Runable接口,重写接口中的run()方法,newThread(传入Runable接口实现类)

3.匿名内部类,new Thread(重写run方法)、newRunable(重写run方法)

4.Callable<T>,类似Runable,不过会返回处理结果,T为返回结果类型

5.线程池提供线程

  2.线程状态

1.新建状态:创建线程后,尚未使用线程执行代码。

2.就绪状态:广义的就绪状态是指新建线程start之后即将进入运行的状态

3.阻塞状态:主动休眠和等待状态、被动被阻塞状态都是阻塞状态

4.运行状态:线程获得CPU运行时间,进入执行的状态。

5.死亡状态:线程执行结束或者未catch异常终止run方法。

  3.线程池

线程池可以看作是一种使用工厂设计思想的简易框架,几乎所有异步并发的多线程程序都可以使用线程池。

线程池作用:1.降低资源消耗,通过重复利用已创建的线程降低线程的创建和销毁造成的内存消耗。

2.提高响应速度,当任务到达时,不需要等待线程创建就能立即执行。

3.提高线程的客观理性,线程不能无限制创建,会消耗系统资源并降低系统的稳定性,使用线程池可以对线程进行统一分配、调优和监控。

       Java为线程池提供了一个类,java.util.concurrent.ThreadPoolExecutor,属于线程池的核心类。其实现了Executor, ExecutorService两个接口,ExecutorService用来执行提交的任务,Executor用来对工厂进行配置。

     线程池的相关配置问题:

A.核心线程池和线程池最大大小。ThreadPoolExecutor通过setCorePoolSize(int)设置核心线程数和setMaximumPoolSize(int)设置循序最大线程数来调整线程池大小。当CorePoolSize<MaximumPoolSize,当队列满时会创建新线程,当CorePoolSize=MaximumPoolSize时,线程池为固定大小。

B.按需构造。默认情况下,核心线程最初只是在新任务需要时才创建和启动,可以使用方法品restartCoreThread()或prestartAllCoreThreads()对其进行动态重写。

C.创建新线程。线程池创建新线程使用Executors.defaultThreadFactory()的方法,新线程默认具有NORM_PRIORITY优先级和非守护进程状态。

D.线程池中线程活动时间。线程池中的非核心线程会在空闲时间超过某个时间时进行销毁,用来减少非活动状态下对系统资源的消耗,如果核心线程队列满时,自动创建新线程。

E.排队。如果运行线程小于核心线程数,Executor首选添加新线程,不进行排队;如果运行线程等于或多余核心线程数,Executor首选请求加入队列,不添加新线程;如果无法请求加入队列,则创建新线程,当创建的线程数大于线程池最大线程数时,任务将被拒绝。

                 Executor封装了四种线程池:CachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。FixedThreadPool 创建一个定长线程池,可控制线程最大并发数超出的线程会在队列中等待。ScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。SingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO, 优先级)执行。

  4.多线程并发

JVM中存在一个主存区,主存区中的数据对所有线程进行共享,同时每个线程又拥有自己的工作内存,工作内存的数据是从主存区中拷贝,线程对各自工作内存中的数据进行操作,然后传递至主存区中。线程之间是不能直接相互访问的,依赖主存区进行。某个线程A从主存区中拷贝到数据X,并在自己的工作内存中对X进行处理,但并未及时将处理后的X更新至主内存中,线程B在内存中读取到了主内存中原始的数据X,此时线程B并不知道线程X已存在改变。这种情况称为线程安全问题。

为解决上述的线程安全问题,java提出了线程同步机制:同步代码块和同步方法以及一个接口Lock。

同步代码块:

Synchronized(锁对象){可能产生线程安全的代码}

同步代码块中的锁对象可以是任何对象(不要和接口Lock搞混),但要解决多线程的安全问题,要使用同一锁对象。

同步方法:

public Synchronized void method(){可能产生线程安全的代码}

同步方法中的锁对象是this

接口Lock:锁定lock()和取消锁定unlock()出现在不同作用范围中时,相较于同步代码块和同步方法更灵活,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁定。

死锁:是指多个以上进程在执行的过程中,由于抢占资源或彼此通信造成的一种祖册现象,若无外力作用,它们将无法推进下去。此时成系统处于死锁状态,或产生了死锁,这种相互等待的进程称为死锁进程。

  5.锁机制

1.悲观锁:悲观地认为每次操作都会造成更新丢失问题,也就是说认为别人会拿去修改,一次每次查询数据时都会加上排它锁。这样别人想拿,就会被拒绝直到它拿到锁。使用方法:SQL语句后面添加for update,例如select * from order for update ;原理:只允许一个连接进行操作,当一个线程进入后会锁住,别的线程要等待锁释放才能使用。效果较低。

2.乐观锁:乐观的认为每次查询都不会造成更新丢失,利用版本字段控制。原理:每个数据库都有一个版本号version,当读取数据时,会将version字段一同读出,数据每更新一次,version值就+1,当我们提交更新时,会判定version是不是我们取数据时候的version,如果不是就认为数据过期,会更新失败。乐观锁的第二种实现方法是在表中增加一个类似version的时间字段(这个字段可以不用专门增加,使用表中原有字段也可),原理同version相同。

3.重入锁:以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他线程是不可以的。原理:通过为每个锁关联一个请求计数和一个占有它的线程,当计数为0时,认为锁是未被占有的,线程请求一个未被占有的锁时,jvm将记录该线程,并将计数器置为1,如果同一线程再次请求这个锁时,计数器将+1,每次线程退出同步块时,计数器值将为0,锁被释放。

4.读写锁(一种互斥锁):两个线程同时读一个资源没有问题,但如果一个线程想要更新一个资源,其它线程就不能进行读写操作,也就是说读读共存,读写和写写不共存。

5.CAS无锁:CAS无锁机制是不给一段代码加锁,效率比有锁高。CAS体系有三个参数,V/E/N。V表示要更新的数据,E表示的预期值,N表示新值。当一个线程获取一个数据V,线程将操作后的数据V改为数据N,当V=E时,将数据V修改成数据N,如果V!=E时,当前线程不进行更新或者重新进行操作。举个栗子,数据库有数值=1,线程A将数值改成2,提交时线程的预期值是数据库里的数值为1,才能提交成功,如果数据库数值不为1,则说明有其它线程更改过数据,提交不成功。

6.自旋锁:自旋锁与互斥锁类似,不同点是互斥锁机制中,锁的执行线程执行了,等待调用线程就休眠了,而自旋锁机制中,等待调用线程会在那里等着(将等待调用线程放置在空循环中,让它在那里转圈圈,一圈两圈三四圈),直到锁的执行线程释放了锁。自旋锁比较浪费资源。

7.分段锁:分段锁是锁的一种设计。相当于将一条数据的某一段进行加锁,细化锁的粒度,如果要获得整条数据的操作信息,就需要获取所有的分段锁才能统计。

锁的分类:

按锁的获取顺序来分:公平锁和非公平锁。公平锁是指多线程按照申请锁的顺序来获取锁;非公平锁指多个线程获取锁的顺序不是按照申请锁的顺序进行。非公平锁吞吐量比公平锁大。

按锁被线程持有的权限分:独享锁和共享锁。独享锁是指锁只能被一个线程持有;共享锁指该锁可以被多个线程持有。下面的读写锁中,读读锁为共享锁,读写锁为独享锁。

按照看待并发同步的角度分:悲观锁和乐观锁

按照锁的状态分(只针对Synchronized):偏向锁、轻量级锁、重量级锁。偏向锁指一个线程获取锁时,下一次会自动获取锁,可重入锁机制就属于偏向锁,用来降低获取锁的代价。轻量级锁指偏向锁被另一个线程获取,其它线程会通过自旋锁机制尝试获取锁。重量级锁指轻量级锁中,在自旋的线程自旋到一定次数后就会阻塞,该锁就成了重量级锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值