多线程

1.线程概述

       线程是进程的执行单元,进程具有:独立性,动态性,并发性三个特征。线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程,线程可以有自己的堆栈,自己的计数器,自己的局部变量,但不拥有系统资源,他与父进程的其他线程共享该进程所拥有的全部资源。线程之间的运行是独立性的,但其执行是抢夺式的,但一个线程可以操作其他的线程,同一进程的线程之间可以并发执行。

       简而言之,一个程序运行之后至少有一个进程,一个进程可以包含多个线程,但至少包含一个线程。(操作系统可以同时执行多个任务,每个任务就是进程;进程可以执行多个任务,每个任务就是线程)

      并发:指同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行。

      并行:同一时刻,有多条指令在多个处理器上同时执行。

2.线程的创建与启动

2-1,继承Thread类

      步骤:

      1.定义Thread的子类,重写其run()方法。

      2.实例化该类。

      3.调用线程对象的start()方法来启动该线程。

2-1,实现Runnable接口

      步骤: 

      1.定义Runnable接口的实现类,实现其run()方法。

      2.创建Runnable实现类的实例,并以此实例作为Thread的参数来创建Thread对象。

      3.调用线程对象的start()方法来启动该线程。

2-3,使用Callable和Future创建线程

      步骤:

      1.创建Callable接口的实现类,并实现call()方法。(call()方法将作为线程的执行体,有返回值)创建Callable的实例。

      2.使用FutureTask类来包装Callable对象。(该FutureTask对象封装了Callable对象的call()方法的返回值)

      3.使用FutureTask对象作为Thread对象的参数创建并启动线程。

      4.调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

className rt = new className();

FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{//线程运行的内容});

new Thread(task).start();

task.get();

       注意:Callable接口有泛型限制,Callable接口里的泛型形参类型与call()方法的返回值类型相同。

       而且Callable接口是函数式接口(类似于ES6中声明函数)

2-4.三种方式的比较

       第一中方法,不能再继承其他类,有局限性;第二种方法还可以实现继承其他类,推荐使用;第三种方法也可以继承和实现其它类,但过程有些复杂。

3.线程的控制

3-1.后台线程

       后台线程可以说是为前台线程服务的线程,比如垃圾回收机制。后台线程会在前台线程死亡后自动死亡,当人为强制结束一前台线程时,无论后台线程是否执行完,都会死亡。我们一般自己定义的线程都是前台线程,当然也可以通过Thread的setDaemon(true);方法设置一线程为后台线程,次设置要放在start();方法前。判断一个线程是否为后台线程可以用Thread类的isDaemon();方法。

3-2.线程控制

        首先,线程有新建,就绪,运行,死亡,阻塞这五种状态。

       join()方法:调用此方法的线程对象会一直占用cpu的执行权,知道该线程执行结束,才允许当前其它线程抢夺cpu执行权。

       sleep()方法:让线程睡眠一定的时间,不会释放资源,相当于使该线程进入阻塞状态,没有释放锁。

       yield()方法:让该线程暂停执行,并进入就绪状态,不会阻塞该线程,不释放锁。

       wait()方法:让该线程进入等待状态,在被唤醒之前都不会执行,会释放cpu资源,也释放了锁,也有和sleep一样的时间参数。

       notify()和notifyAll()方法:用来唤醒线程,与wait()方法结合使用。

       等待唤醒必须写在同步代码块中,也就是说等待和唤醒的都是同步的线程。

4.线程的同步

       线程的同步目的是为了解决多个线程对只有一份的资源共同访问带来的资源安全问题,这一问题的原因是线程的执行是抢夺式的。解决这一问题可以通过同步代码块,同步方法和同步锁来解决。

4-1.同步代码块

       只需要在需要的步的代码块外用synchronized(){}包起来,并给其传人加锁的对象,便可以使得同步代码块中的代码在执行时一直占用cpu执行权,在代码执行完后会释放锁。这样别的线程在要操作加锁的对象时,会失败,因为同一时刻只能有一个线程拿到锁。

4-2.同步方法

       同步方法是在一个方法前加上synchronized关键字,使其变为同步方法。其锁是调用此方法的对象,也就是this。

       要注意的是synchronized只能修饰代码块和方法,不能修饰其他。

       锁会在同步代码执行完后,遇到wait()方法或者发生错误后释放,在其他情况下都不会释放锁。

4-3.同步锁

       同步锁是Java 5后通过显示的定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当。

private final ReentrantLock lock = new ReentrantLock( );

       ReentrantLock(可重入锁)是Lock接口的实现类,Lock,ReadWriteLock是Java 5提供两个根接口,ReentrantReadWriteLock是ReadWriteLock接口的实现类,ReentrantReadWriteLock类为读写操作提供了三种锁模式:Writing,ReadingOptimistic,Reading。Java 8还增加了StampedLock类。

       在使用是通过lock.lock()和lock.unlock()将同步的代码包起来就可以了。

5.线程的通信

5-1.等待唤醒机制

       wait()和notify()这两方法在Object中,没在Thread中,同时等待唤醒必须写在同步代码块中,也就是说等待和唤醒的都是同步的线程。

5-2.使用Condition控制线程通信

       如果线程中没有synchronized关键字来保证同步,而是使用Lock来同步,就可以使用Condition类,他可以让得到Lock对象却无法继续执行的线程释放Lock对象。

private final ReentrantLock lock = new ReentrantLock( );

private final Condition cond = lock.newCondition( );

       这里主要有三个方法和wait(),notify(),notifyAll()相对应,await(),signal(),signalAll()。因为Condition将前三个方法分解成了其他不同的对象。

5-3.使用阻塞队列控制线程通信(不好用)

       Java提供了一个BlockingQueue接口,他是Queue的子接口,主要用途是作为线程同步的工具,而不是容器。他有一个特征,当线程要向队列放入元素时,如果队满,则线程被阻塞;当线程从队列中取元素时,如果队列以空,则线程进入阻塞。这样就可以控制线程的阻塞。

       BlockingQueue提供了两个支持阻塞的方法:put(E e)和take(E e)。

6.线程池

       当系统启动一个新线程的成本是比较高的(创建新的PCB等),在使用线程池后可以很好的提高性能。

       线程池在系统启动时会创建大量空闲的线程,当一个Runnable或Callable对象传给线程池时,线程池会启动一个线程来执行他们的run()或call()方法,当run()或call()方法执行完后,该线程不会死亡,而是再次返回线程池中成为空闲状态,等待下一次启动。

       使用线程池也可以有效的控制系统中并发线程的数量。具体的使用请看我的另一篇文章:Java线程池(未写)。

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

推荐阅读更多精彩内容