Java 并发基础

一、概念理解

(1)多线程的两种实现形式:一种是实现Runnable接口,一种是继承Thread类并重写其run方法

(2)线程的执行的两种形式:一种是使用ExecutorService线程池,一种是使用Thread的start

(3)ExecutorService(具有服务生命周期的线程执行器)的三种实现:CachedThreadPool(为每个任务创建一个线程),FixedThreadPool(创建有限数量的线程),SingleThreadExector(只创建一个线程,多个线程顺序执行)

(4)Thread.yield()是对线程调度器的一种建议:我已经执行完了生命周期中的最重要的部分可,此刻是CPU切换给其他任务执行一段时间的大好时机。不过这只是一个暗示,没有任何机制能保证它会被采纳。所以,不可过度依赖yield。

(5)Thread.join()是先执行另一个线程,待另一个线程执行完后再继续执行下边的代码,强制线程按照先后顺序执行。

(6)线程的休眠Sleep,应该使用TimeUnit.SECOND.sleep(200)的写法而不是使用Thread.sleep(),前者允许指定sleep()的延迟的时间单元,具有更好的可读性。

(7)线程的优先级通过Thead.setPriority()来实现,因为JDK的十个优先级与多数操作系统不能映射的很好,所以当调整优先级的时候,只使用MAX_PRIORITY(最高)、NORM_PRIORITY(普通)、MIN_PRIORITY(最低)三种级别

(8)后台线程daemon,又叫守护线程,是服务于其他线程的一种后台服务线程,当他服务的线程中的所有非守护线程结束时,程序也就终止了,守护线程也会被全部杀死。也就是说,守护线程必须依附于别的线程存活。可以通过setDaemon()方法来设置是否为守护线程

(9)Executors.newCachedThreadPool()方法接收一个ThreadFactory参数,可以由开发者自己指定Thread的创建方式以及参数的设置

(10)要捕获线程的异常,可以通过在ThreadFactory中调用Thread.setUncaughtExceptionHandler()方法来处理异常,这个方法接收一个Thread.UncaughtExceptionHandler对象作为参数.

(11)Brian的同步规则:如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你必须使用同步,并且,读和写线程都必须使用相同的监视器锁同步。

(12)线程的四种状态:新建(new),就绪(Runnable),阻塞(Blocked),死亡(Dead)。

二、重要概念区分

(1)Runnable执行独立任务,不会返回任何值;Callable是实现有返回值的任务。Runnable接口使用ExectorService的execute方法执行,而Callable接口使用ExectorService的submit方法执行,该方法返回一个带参数类型的Future对象,调用Future的get方法可获取返回值。

(2)sleep()和wait()的区别:sleep是任务进入休眠状态,这时该线程并不会释放锁(yield方法也是如此),而wait的时候线程被挂起,同时也释放锁,共享的资源可以被其他线程使用。

(3)notify和notifyAll的区别:如果有多个线程在等待一个对象的锁,notify会唤醒其中一个线程(随机唤醒),其他线程继续等待,只有被唤醒的这个线程可以执行完;但是notifyAll会唤醒所有线程,虽然还是只有一个线程会获得锁继续执行,不一样的是,这个线程释放锁之后其他的线程会竞争获取这个锁继续执行,最终所有的线程都会执行完。

(4)ExecutorService的shutdown()方法和shutdownNow()方法的区别:shutdown调用后,不可以再submit新的task,已经submit的将继续执行;shutdownNow试图停止当前正执行的task,并返回尚未执行的task的list。

三、并发锁机制的几种实现:

(1)在方法上使用synchronized关键子。注意:对于某个特定对象来说,其所有synchronized方法共享同一个锁,这可以被用来防止多个任务同时访问该对象。其实意思就是说,锁是加在某个特定对象上的,而不是某个方法上

(2)使用锁的另一个形式是使用Lock对象,Lock lock = new ReentrantLock(); try{lock.lock();}finally{lock.unlock();},这种方式更灵活,但是不好控制,一般情况是使用synchronize即可。

(3)第三种使用锁的形式是使用synchronize块,synchronize(object){},这种方式可以很好的解决一部分性能问题,除非你觉得自己可以搞定,否则还是用synchronize关键字吧。

(4)第四种解决同步的方式是使用volatile关键字,但是当一个域的值依赖于他之前的值时,或者是受到其他域的值的限制事,volatile就失效了,比如a++的操作或有a<b这样的边界限制,volatile都是无法工作的。使用volitile而不是synchronize的唯一安全的情况就是类中只有一个可变的域,所以,最安全的操作还是使用synchronize关键字。

(5)第五种解决同步的方式是使用线程的本地存储,即把需要同步的数据让ThreaLocal对象持有,ThreadLocal<Integer> value = new ThreadLoal<Integer>{protected synchronized Integer initaValue(){return new Integer(1);}}

四、常用多线程相关类

(1)java中的一些原生的原子类:AtomicInteger、AtomicLong、AtomicReference等

(2)jdk自带多线程解决方案:LinkedBlockingQueue(无界队列),ArrayBlockingQueue(固定尺寸的队列,满了后线程就会被阻塞),PipedWrite和PipedReader(通过输入/输出在线程间进行通信),CountDownLatch(一组任务必须执行完成后才能执行下一组,一次性的),CyclicBarrier(在进行下一个步骤之前,所有的前置任务必须都完成,像CountDownLatch,但是是可以多次重复的,典型的赛马),DelayQueue(一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走,可以用来实现session超时),PriorityBlockingQueue(一个优先级队列,可对其中的任务按照优先级排队取出,具有可阻塞的读取操作),ScheduleExecutor(定时执行任务或每隔规则的时间重复任务),Semaphore(允许N个任务同时访问一个资源),Exchanger(两个任务交换对象的栅栏)

(3)容器性能优化:使用CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap、ConcurrentLinkedQueue等免锁容器来实现高效的同步解决方案,在容器需要同步的时候尽量使用这些对象,可以很大的提升性能,减少锁带来的性能损耗

(4)ReadWriteLock对写入不频繁,但是读取特别频繁的数据同步进行了优化,只有在这种情况下才考虑使用它。在它执行写入操作的时候,所有线程都不可读取。

五、其他

(1)原子性:除了long和double之外的基本类型(int、char、byte、short、boolean、float)的读取和写入操作,可以保证其原子性,其他的操作都不是原子性的。因为long和double都是64位的,会被JVM当作两个分离的32位来进行操作。如果要实现long和double的原子性,可以使用AtomicLong类型和AtomicDobule类型,他们本身进行了原子性封装。但是64位的系统就可以保证long和double的原子性。

(2)错失的信号:要切记原子性规则,如果有第二个线程的wait方法在第一个线程的notify方法之后执行的可能性,就有可能会错失信号,导致死锁。一般synchronize块里边包含while语句,而不是用while包含synchronize块。

(3)正确理解notifyAll方法:当notifyAll因某个特定锁而被调用时,只有等待这个锁的任务才会被唤醒。就是说,notifyAll只会唤醒对特定某个对象上锁的线程。

(4)死锁,要形成死锁,必须同时满足下面四个条件:1)互斥条件,任务间必须共享至少一个资源;2)至少有一个任务必须持有一个资源且正在等待获取一个当前被别的任务持有的资源;3)资源不能被任务抢占,一个任务不能从另一个任务那里抢占资源;4)必须有循环等待,就会形成多任务见循环等待,都在等待别人,而没有人释放资源,就形成了死锁。

(5)中断,Thread.interrupted()方法或ExecutorService.shutdownNow()可以中断线程的sleep阻塞,但是不能中断I/O阻塞和互斥锁所阻塞,I/O中断可以通过清理资源的方式来中断。

(6)另一种线程调度方式是使用Condition类,Condition.await()挂起任务,Condition.signal()和signalAll()用来唤醒被其自身挂起的任务,一般跟Lock配合使用。Condition condition = new ReentrantLock().newCondition();

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容

  • Java并发总结 1.多线程的优点 资源利用率更好 程序在某些情况下更简单 程序响应更快 2.创建线程 1.实现R...
    不会上树的猴子阅读 1,013评论 0 5
  • 摘要: 我们已经知道,synchronized 是Java的关键字,是Java的内置特性,在JVM层面实现了对临界...
    kingZXY2009阅读 1,828评论 0 20
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • 一个简单的单例示例 单例模式可能是大家经常接触和使用的一个设计模式,你可能会这么写 publicclassUnsa...
    Martin说阅读 2,207评论 0 6
  • 一、并发 进程:每个进程都拥有自己的一套变量 线程:线程之间共享数据 1.线程 Java中为多线程任务提供了很多的...
    SeanMa阅读 2,396评论 0 11