Java中的线程---基础知识

今天刚刚参加了一次面试,被面试官的关于线程的提问生生碾压了。重整旗鼓,总结一下关于线程的基础知识,相信也可以帮到一批小伙伴的。首先分享一下基础知识。南玻万:0

线程与进程的区别

区别详解请移步链接中的文章已经把区别整理的很完善了,同学们可以自行移步查看。我在这里写一下我自己总结的它们之间的区别:

  • 它们是不同的操作系统管理资源的方式。
    • 进程是系统进行资源分配和调度的一个独立单位;
    • 线程是CPU进行资源分配和调度的基本单位;
  • 一个程序至少有一个进程,一个进程又至少有一个线程。
  • 进程有自己独立的地址空间,一个进程崩溃后,一般不会对其他进程产生影响。而线程只是一个进程的不同执行路径,线程有自己的堆栈和局部变量,但线程没有单独的地址空间。一个线程死掉就等于整个进程死掉,所以多进程的程序比多线程的程序更健壮。但进程切换时,耗费资源较大,效率要差一些。

线程的状态

下面这张图片展示了线程的所有状态和状态之间的互相转换:


线程状态图
  • 可运行(runnable):线程创建后调用了.start()方法。该线程位于可运行的线程池中,等待被线程调度选中,获得CPU的资源;
  • 运行(running):可运行状态(runnable)的线程获得了CPU的时间片(timeslice),执行线程内的代码;
  • 阻塞(block):阻塞状态是指线程暂时放弃了CPU的使用权,即让出了CPU的时间片。阻塞状态的线程直到转换成可运行状态进入到可运行线程池中,才会有机会再次获得CPU的时间片进入到运行状态。使线程进入阻塞状态的情况由一下几种:
    • 等待阻塞:线程执行了wait()方法,JVM会将线程放入到等待队列(waitting queue)中。
    • 同步阻塞:运行的线程在获取对象的同步锁时,如该同步锁被别的线程占用,则JVM会把该线程放入到锁池(lock pool)中。
    • 其他阻塞:运行中的线程执行了Thread.sleep()方法或b.join()方法,或者当前线程执行到了I/O操作时。JVM会把线程切换到阻塞状态,当sleep()方法休眠结束时、b线程执行完成后,I/O操作完成后,该线程又会转换到Runnable状态。
  • 死亡(dead):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

同步锁(synchonrize)的介绍

相信很多同学应该都和我差不多只知道这个关键字的简单实用,对于很多细节其实并不是很清楚,这里我同大家一起分享关于同步锁的正确使用。
首先我们大家都应该知道每个对象都有一个锁标志,被synchonrize关键字修饰的变量或方法将被上锁,同一时刻只能被单一线程访问。当前线程访问完数据后释放锁标志,其他线程才可以进行访问。这里还需要给大家补充一下线程常用方法与同步锁之间存在的关系,以下表格可以清晰的展示这一点:

方法 是否释放锁 备注
wait wait和notify/notifyAll是成对出现的, 必须在synchronize块中被调用
sleep 可使低优先级的线程获得执行机会
yield yield方法使当前线程让出CPU占有权, 但让出的时间是不可设定的

对以上方法的备注我还引入一些更具体的解释(点击给进入原作者的文章)。如下:

  • sleep()
    使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
    例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
    总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。

  • yield()
    该方法与sleep()类似,只是不能由用户指定暂停多长时间。这里还需补充一点,yield()方法对应了如下操作: 先检测当前是否有相同优先级的线程处于同可运行状态, 如有, 则把 CPU 的占有权交给此线程, 否则继续运行原来的线程.。所以yield()方法称为“退让”, 它把运行机会让给了同等优先级的其他线程。

  • wait()和notify()、notifyAll()

这三个方法用于协调多个线程对共享数据的存取,wait()有出让Object锁的语义, 要想出让锁, 前提是要先获得锁, 所以要先用synchronized获得锁之后才能调用wait(), notify原因类似。
wait()方法使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。

同步锁(synchonrize)的使用

synchronize关键字主要有下面5种用法

  1. 在方法上进行同步, 分为(1)instance method/(2)static method, 这两个的区别后面说
  2. 在内部块上进行同步, 分为(3)synchronize(this), (4)synchonrize(XXX.class), (5)synchonrize(mutex)

代码示例如下:

private int value = 0;
private final Object mutex = new Object();
public synchronized int incAndGet0() {
       return ++value;
 }
public static synchonrize int incAndGet1();
 public int incAndGet2() {
        synchronized(this){
            return ++value;
        }
    }
public int incAndGet3() {
       synchronized(SyncMethod.class){
            return ++value;
        }
    }
public int incAndGet4() {
       synchronized(mutex){
            return ++value;
        }
    }

  1. 作为修饰符加在方法声明上, synchronized修饰非静态方法时表示锁住了调用该方法的堆对象, 修饰静态方法时表示锁住了这个类在方法区中的类对象。
  • 关于Java中的堆、栈和方法区,请大家移步很详细的介绍了以上内容,感觉作为程序员真是学海无涯啊。
  1. synchronized(X.class) 使用类对象作为监控。 同一时间只有一个线程可以能访问块中资源。
  2. synchronized(this)和synchronized(mutex) 都是对象锁, 同一时间每个实例都保证只能有一个实例能访问块中资源。

以上就是关于synchonrize关键字的基础知识。其他还有一些关于synchronized和volatile比较synchonrize和juc中的锁比较
这里也简单介绍一下:

  • volatile
    锁提供了两种主要特性:互斥(mutual exclusion)可见性(visibility)
    • 互斥,即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。
    • 可见性,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。
      Volatile 变量具有 synchronized的可见性特性,Volatile是轻量级的synchronized。

当且仅当下面条件全部满足时, 才能使用volatile

  1. 对变量的写入操作不依赖于变量的当前值, (++i/i++这种肯定不行), 或者能确保只有单个线程在更新
  1. 该变量不会与其他状态变量一起纳入不变性条件中
  2. 访问变量时不需要加锁
  • ReentrantLock
    ReentrantLock在内存上的语义于synchronize相同, 但是它提供了额外的功能, 可以作为一种高级工具. 当需要一些 可定时, 可轮询, 可中断的锁获取操作, 或者希望使用公平锁, 或者使用非块结构的编码时 才应该考虑ReetrantLock。

以上就是这两种锁的比较的简单介绍,想要深入学习的同学请大家自行搜索学习吧。最后送给正在进行面试同学们的一句话:面试官虐我千百遍,我待面试官如初恋。---必须正能量,清喷。

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

推荐阅读更多精彩内容

  • 一、进程和线程 进程 进程就是一个执行中的程序实例,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。...
    阿敏其人阅读 2,606评论 0 13
  • 该文章转自:http://blog.csdn.net/evankaka/article/details/44153...
    加来依蓝阅读 7,331评论 3 87
  • 写在前面的话: 这篇博客是我从这里“转载”的,为什么转载两个字加“”呢?因为这绝不是简单的复制粘贴,我花了五六个小...
    SmartSean阅读 4,709评论 12 45
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,949评论 1 18
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,438评论 1 15