Java多线程


线程是指程序在执行过程中,能过执行程序代码的一个执行单元。在Java中,线程有4种状态:运行、就绪、挂起和结束。
进程是指一段正在执行的程序。而线程有时被称为轻量级进程,它是程序执行的最小单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间(代码段、数据段和堆空间)及一些进程级的资源(如打开文件),但是各个线程拥有自己的栈空间。


进程和线程的关系.png

多线程的使用的好处:

  • 使用多线程可以减少程序的响应时间。
  • 与进程相比,线程的创建和切换开销更小。由于启动一个新的线程必须给这个线程分配独立的地址空间,建立许多数据结构来维护线程代码段、数据段等信息,而运行于同一个进程内的线程共享代码段、数据段,线程的启动或切换的开销比进程少很多。同时多线程在数据共享方面效率非常高
  • 多CPU或多核计算机本身就具有执行多线程的能力,在这些计算机上使用多线程能提高CPU的利用率。
  • 使用多线程能简化程序的结构,使程序便于理解和维护。

实现多线程的常用方法:

  • 继承Thread类,重写run()方法;
    Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法是通过Thread类的start()方法。start()方法是一个本地方法,它将启动一个新线程,并执行run()方法(Thread中提供的run()方法是一个空方法)。这种方式通过自定义直接extends Thread,并重写run()方法,就可以起到新线程并执行自己定义的run()方法。调用start()方法后并不是执行多线程代码,而是使用该线程变为可运行态(Runnable),什么时候运行多线程代码由操作系统决定。

  • 实现Runnable接口,并实现该接口run()方法;

    • 自定义类并实现Runnable接口,实现run()方法。
    • 创建Thread对象,用实现Runnable接口的对象作为实例化该Thread对象。
    • 调用Thread的start()方法。
class MyThread implements Runnable{
    public void run(){
        System.out.println("Thread body");
    }
}
public class Test{
    public static void main(String []args) {
        MyThread thread = new MyThread():
        Thread t = new Thread(thread);
        t.start();
    }
}

不管是通过继承Thread类还是通过使用Runnable接口实现多线程的方法,最终还是通过Thread的对象的API来控制线程的。

  • 实现Callable接口,重写call()方法
    Callable泛型接口实际是属于Executor框架中的功能类,有一个泛型参数V,Callable接口与Runnable接口的功能类似,但提供了比Runnable更强大的功能。
    • Callable可以在任务结束后提供一个返回值(类型为V)的call()函数,Runnable无法提供这个功能。
    • Callable中的call()方法可以抛出异常,而Runnable无法提供这个功能。
    • 运行Callable可以拿到一个Future对象,Future对象表示异步计算的结果,它提供了检查计算是否完成的方法。由于线程属于异步计算模型,因此无法从别的线程中得到函数的返回值,在这种情况下,就可以使用Future来监视目标线程调用call()方法的情况,当调用Future的get()方法以获取结果时,当前线程会阻塞,直到call()方法结束返回结果。
public class CallableAndFuture{
    //创建线程类
    public static class CallableTest implements Callable<String> {
          public String call() throws Exception{
               return "Hello world!"
          }
    }
    public static void main(String []args) {
        ExecutorService threaPool = Executor.newSingleThreadExecutor();
        //启动线程
        Future<String> future = threadPool.submit(new CallableTest());
        try{
            System.out.println("waiting thread to finish!");
             System.out.println(future.get());//等待线程结束,并获取返回结果
        }
    }
}

注:
一般推荐实现Runnable接口的方式,原因是:

  • Thread类定义了多种方法可以被派生类使用或重写。但是只有run()方法是必须被重写的,在run()方法中实现这个线程的主要功能。这当然是实现Runnable接口所需要的方法。
  • 很多人认为一个类仅在他们需要被加强或修改时才会被继承,因此,如果没有毕业重写Thread类中的其他方法,那么通过继承Thread的实现方式与实现Runnable接口的效果相同,在这种情况下最好通过实现Runnable接口的方式来创建线程。

run()方法与start()方法有什么区别
系统通过调用线程类的start()方法来启动一个线程,此时该线程处于就绪状态,而非运行状态,也就意味着这个线程可以被JVM来调度执行,在调度过程中,JVM通过调用线程类的run()方法来完成实际的操作做,当run()方法结束后,此线程就会终止。


多线程同步的实现方法

  • synchronized关键字
    在Java语言中,每个对象都有也给对象锁与之关联,该锁表明对象在任何时候只允许被一个线程所拥有,当一个线程调用对象的一段synchronized代码时,需要先获取这个锁,然后去执行相应的代码,执行结束后,释放锁。
    synchronized关键字主要有两种用法(synchronized方法和synchronized块),还可以用于静态方法、类或某个实例,但对程序的效率影响很大。
    • synchronized方法。在方法的声明前加入synchronized关键字。只要把多个线程对类需要被同步的资源的操作放到synchronized方法中,就能保证这个方法在同一时刻只能被一个线程访问,从而保证了多线程访问的安全性。然而,当一个方法的方法体规模非常大时,把该方法声明为synchronized会大大影响程序的执行效率。
    • synchronized块。synchronized块既可以把任意的代码段声明为synchronized,也可以指定上锁的对象,有非常高的灵活性。
synchronized(syncObject){
    //访问syncObject的代码
}
  • wait()方法和notify()方法
    在synchronized代码被执行期间,线程可以调用对象的wait()方法,释放对象锁,进入一个和该对象相关的等待池中,并且可以调用notify()方法或notifyAll()方法通知正在等待的其他线程可以访问。notify()方法仅唤醒一个线程(等待对列中的第一个线程)并允许它去获得锁,notifyAll()方法唤醒所有等待这个对象的线程并允许它们去获得锁(所有线程根据优先级获得锁)。必须放在synchronized块中。
  • Lock
    • lock()以阻塞的方式获取锁,如果获得锁,立即返回;如果别的线程持有锁,当前线程等待,直到获得锁后返回。
    • tryLock()以非阻塞的方式获取锁,只是尝试性地获取一下锁,如果获取到锁,立即返回true;否则,返回false。
    • tryLock(long timeout, TimeUnit unit)如果获取了锁,返回true。否则等待参数定的时间单元,在等待的过程中,如果获取锁,返回true;如果超时,返回false。
    • lockInterruptibly()如果获取锁,立即返回true,如果没有,当前线程处于休眠状态,直到获得锁。

线程的wait()、sleep()、join()、yeild()

  • wait()上述
  • sleep()是Tread类的静态方法,作用是使调用线程进入睡眠状态。因为是Thread类的静态方法,因此它不能改变对象的机锁。所以,当一个synchronized块中调用sleep()方法时,线程虽然休眠了,但是对象的机锁并没有被释放,其他线程无法访问这个对象。sleep()方法必须捕抓异常,
  • join()等待目标线程执行完成之后 再继续执行
  • yeild()线程礼让。目标线程有运行状态转换为就绪状态也就是让出执行权限,让跟其他线程得以优先执行,只会给相同优先级或更高优先级的线程以运行机会。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容

  • 该文章转自: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
  • 上一章 “我们家又不是孤儿院……而且根本说是对小孩影响很不好啊。”女人扶着额头很头疼的样子。 “我不也是你捡来的?...
    Diedorkce阅读 446评论 0 0