• 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html
  • 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html

多线程复习之并发编程基础与原子操作

技术杂谈 巷子的童年 2周前 (03-10) 31次浏览 已收录 0个评论 扫描二维码
一.并发编程基础
1.创建线程
法一:继承Thread方法,并且覆写其run()方法;
法二:实现Runnable接口(策略模式),这种方式实现使程序的扩展性更好。
Thread的构造方法:public Thread(ThreadGroup group, Runnable target, String name, long stackSize);
线程组(ThreadGroup):默认为和父线程组相同。
2.线程的生命周期
新建(new Thread):创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
就绪(runnable):线程已经被启动(start()已经执行),正在等待被分配给CPU时间片
运行(running):线程获得CPU资源,正执行任务(run()方法)。
堵塞(blocked):由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
当发生如下情况时,线程将会进入阻塞状态:
① 线程调用sleep()方法主动放弃所占用的处理器资源;
② 线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞;
③ 线程试图获得一个同步监视器(monitor),但该同步监视器正被其他线程所持有;
④ 线程在等待某个通知(notify);
⑤ 程序调用了线程的suspend()方法将该线程挂起(容易导致死锁)。
死亡(dead):当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
线程会以如下3种方式结束,结束后就处于死亡状态:
① run()或call()方法执行完成,线程正常结束
② 线程抛出一个未捕获的Exception或Error。
③ 直接调用该线程stop()方法来结束该线程——该方法容易导致死锁。
3.控制线程
1)join:当某个线程A的执行流中调用另一个线程B的join方法,则线程A将会阻塞,直到线程B执行完join(long millis):让一个线程最多等待join线程多少毫秒。
2)后台线程(守护线程):程序运行的时候在后台提供一种通用服务的线程,只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会抛出一个IllegalThreadStateException异常。不能把正在运行的常规线程设置为守护线程。
(2) 在Daemon线程中产生的新线程也是Daemon的。 
(3) 不要认为所有的应用都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑。
3)线程睡眠sleep(),线程睡眠,使其进入阻塞状态。
4)线程让步yield(),线程让步,使其从运行状态进入就绪状态(不是阻塞状态)。
5)改变线程优先级,优先级高的执行机会多,默认与父线程的优先级相同,main线程优先级为普通级别。
6)suspend
缺点:a. suspend不会释放锁,在使用时容易造成公共资源的独占以及死锁;
b. 导致数据不同步
4.关闭线程
1)使用标识位关闭(一般是退出while);
2)使用stop()强制停止。突然停止使得一些清理性的工作可能无法执行;执行stop时会立即释放锁使得数据得不到同步处理。
3)使用interrupt()(a、如果线程阻塞,如采用sleep(),线程将捕捉异常并退出阻塞。 b、在while中使用isInterrupted)。
二.原子操作
1.CAS(Compare and Swap)
CAS指的是现代CPU广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。这个指令会对内存中的共享数据做原子的读写操作。首先,CPU 会将内存中将要被更改的数据与期望的值做比较。然后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。否则不做操作并将旧的数值返回。这一系列的操作是原子的。
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V。这是一种乐观锁的思路,它相信在它修改之前,没有其它线程去修改它,如果有被修改再去做其它处理;而Synchronized是一种悲观锁,它认为在它修改之前,一定会有其它线程去修改它,所以每次操作都必须加锁,悲观锁效率很低。
CAS缺点:CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操作。
1)ABA问题
因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。
2)循环时间长开销大
自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
3)只能保证一个共享变量的原子操作
当对一个共享变量执行操作时,可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这时可以用锁,或者把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。
2.i++分为三个阶段:
内存到寄存器;寄存器自增;写回内存;
这三个阶段中间都可以被中断分离开。

丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:多线程复习之并发编程基础与原子操作
喜欢 (0)
[247507792@qq.com]
分享 (0)

您必须 登录 才能发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ


QQ:2248886839


工作时间:09:00-23:00