java || 多线程初步学习

先明确一下几个概念:
1.

  • 多线程:一个程序(进程)在运行的时候产生了不止一个线程。

  • 并行:多个CUP或多台机器同时运行一段逻辑,真正的同时进行。

  • 并发:通过CPU调度算法(如 时间片),让用户看上去是在同时进行。

  • 线程安全:在并发的情况下,线程调度不会影响程序执行的结果。反过来,线程不安全时,线程调度可能会导致变量等修改混乱,影响最终执行结果的正确性。

  • 同步:java中的同步是指通过人为的控制,使多线程运行正确,得出正确的结果。

2.volatile:
首先,多线程模型分为:main memory,working memory。

多线程是会缓存的,所以多线程中的变量最可能遇到的问题是:变量被不同线程修改的读值问题。(如:a变量已经被b线程修改了,但是因为a变量之前已经被缓存过,所以之后c线程读取到的a还是缓存的旧值)。

基于这种情况,volatile关键字的作用就是:不去缓存,直接取值。
更详细的说就是:通常在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去,如图。而volatile关键词的作用:每次针对该变量的操作都激发一次load and save。
在这里插入图片描述
3.基本线程类指的是:Thread类,Runnable接口,Callable接口。
Thread类实现了Runnable接口

  • 启动一个线程的方法:
MyThread mythead = new MyThread()
mythread.start();
  • 中断:中断会影响到线程的wait状态,sleep状态,join状态。被打断后的线程抛出InterruptedException异常。

Thread.interrupted()方法检查当前线程是否发生中断,返回boolean。

线程在synchronized获锁过程中不能被中断。

中断是一个状态!interrupt()方法只是将这个状态置为true而已。所以说正常运行的程序不去检测状态,就不会终止,而wait等阻塞方法会去检查并抛出异常。如果在正常运行的程序中添加while(!Thread.interrupted()) ,则同样可以在中断后离开代码体。

  • 不能在try catch结构的catch来获取线程的异常,可以在try结构中用thread.setUncaughtExceptionHandler(),在这个方法中重写uncaughtException()方法。
    例:
    在这里插入图片描述
  • Runnable:
    与Thread类似。
  • Callable:
    future模式:并发模式的一种,可以有两种形式,即无阻塞和阻塞,分别是isDone和get。其中Future对象用来存放该线程的返回值以及状态
ExecutorService e = Executors.newFixedThreadPool(3);

 //submit方法有多重参数版本,及支持callable也能够支持runnable接口类型.
Future future = e.submit(new myCallable());

future.isDone() //return true,false 无阻塞

future.get() // return 返回值,阻塞直到该线程运行结束
  • 高级多线程类
    接下来是实际项目中常用到的工具了,Java1.5提供了一个非常高效实用的多线程包:java.util.concurrent, 提供了大量高级工具,可以帮助开发者编写高效、易维护、结构清晰的Java多线程程序。

1.ThreadLocal类
作用:保存线程的独立变量。

2.原子类(AtomicInteger、AtomicBoolean……)
使用atomic wrapper class如atomicInteger,或者使用自己保证原子的操作,则等同于synchronized。
如:

//返回值为boolean
AtomicInteger.compareAndSet(int expect,int update)

AtomicReference
对于AtomicReference 来讲,也许对象会出现,属性丢失的情况,即oldObject == current,但是oldObject.getPropertyA != current.getPropertyA。
这时候,AtomicStampedReference就派上用场了。这也是一个很常用的思路,即加上版本号。

3.Lock类
在java.util.concurrent里有三个实现:

ReentrantLock
ReentrantReadWriteLock.ReadLock
ReentrantReadWriteLock.WriteLock

主要目的是和synchronized一样, 两者都是为了解决同步问题,处理资源争端而产生的技术。功能类似但有一些区别。

lock更灵活,可以自由定义多把锁的枷锁解锁顺序(synchronized要按照先加的后解顺序)
提供多种加锁方案,lock 阻塞式, trylock 无阻塞式, lockInterruptily 可打断式, 还有trylock的带超时时间版本。
本质上和监视器锁(即synchronized是一样的)
能力越大,责任越大,必须控制好加锁和解锁,否则会导致灾难。
和Condition类的结合性能更强大。

ReentrantLock    
可重入的意义在于持有锁的线程可以继续持有,并且要释放对等的次数后才真正释放该锁。
使用方法是:

1.先new一个实例

ReentrantLock r=new ReentrantLock();

2.加锁

r.lock()或r.lockInterruptibly();

此处也是个不同,后者可被打断。当a线程lock后,b线程阻塞,此时如果是lockInterruptibly,那么在调用b.interrupt()之后,b线程退出阻塞,并放弃对资源的争抢,进入catch块。(如果使用后者,必须throw interruptable exception 或catch)

3.释放锁

r.unlock()

必须做!何为必须做呢,要放在finally里面。以防止异常跳出了正常流程,导致灾难。这里补充一个小知识点,finally是可以信任的:经过测试,哪怕是发生了OutofMemoryError,finally块中的语句执行也能够得到保证。

ReentrantReadWriteLock

可重入读写锁(读写锁的一个实现)

   ReentrantReadWriteLock lock = new ReentrantReadWriteLock()
  ReadLock r = lock.readLock();
  WriteLock w = lock.writeLock();

两者都有lock,unlock方法。写写,写读互斥;读读不互斥。可以实现并发读的高效线程安全代码

4.容器类
这里就讨论比较常用的两个:

BlockingQueue
ConcurrentHashMap

BlockingQueue
阻塞队列。该类是java.util.concurrent包下的重要类,通过对Queue的学习可以得知,这个queue是单向队列,可以在队列头添加元素和在队尾删除或取出元素。类似于一个管 道,特别适用于先进先出策略的一些应用场景。普通的queue接口主要实现有PriorityQueue(优先队列),有兴趣可以研究

BlockingQueue在队列的基础上添加了多线程协作的功能:

在这里插入图片描述
BlockingQueue

除了传统的queue功能(表格左边的两列)之外,还提供了阻塞接口put和take,带超时功能的阻塞接口offer和poll。put会在队列满的时候阻塞,直到有空间时被唤醒;take在队 列空的时候阻塞,直到有东西拿的时候才被唤醒。用于生产者-消费者模型尤其好用,堪称神器。

常见的阻塞队列有:

ArrayListBlockingQueue
LinkedListBlockingQueue
DelayQueue
SynchronousQueue

ConcurrentHashMap
高效的线程安全哈希map。请对比hashTable , concurrentHashMap, HashMap。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值