多线程/概念/生命周期

概念:
进程是资源分配的最小单位,线程是CPU调度的最小单位。不同的进程使用不同的内存空间。一个进程可以包括多个线程。一个进程的内存空间对他的每个线程是共享的。每个线程有自己的执行堆栈和程序计数器为其执行上下文。


(1)需要频繁创建销毁的优先用线程
(2)需要进行大量计算的优先使用线程
(3)强相关的处理用线程,弱相关的处理用进程
(4)可能扩展到多机分布的用进程,多核分布的用线程。
例1:比如迅雷等多线程下载工具就是典型的多线程。一个下载任务进来,迅雷把文件平均分成10份,然后开10个线程分别下载。这时主界面是一个单独的线程,并不会因为下载文件而阻塞,这里这10个线程是共享内存和其他资源的,所以他们可以同时对迅雷打开的这个文件进行读写。要是10个进程就不行了,因为进程之间的数据是相互独立的。
例2:Chrome浏览器就是一个多进程程序,里面的每个标签页,扩展,插件都是单独的进程,各自独占资源,相互隔离,一个进程出错崩溃只会影响一个页面或者插件,再也不会出现Flash插件出错导致整个浏览器崩溃的情况了。
由于不同的进程拥有不同的地址空间,进程间的数据是相互独立的,在确保数据安全的同时,也为数据的共享带来了一定的苦难。为了让不同的进程间可以交换数据,所以提出了IPC这个概念,即进程间通信。
在Java中,如果你的程序是多线程的,则实际上是JVM负责调用操作系统的多线程接口来实现多线程,即一个JVM实例中运行着多个线程 。而Java的多进程则需要运行多个JVM,每开启一个子进程则需要新的JVM实例来运行,这样如果有一个进程发生异常,并不影响其它的子进程。

创建:

class MyThread extends Thread{
    private static int num = 0;
    public MyThread(){
        num++;
    }
    @Override
    public void run() {
        System.out.println("主动创建的第"+num+"个线程");
    }
}

public class Test {
    public static void main(String[] args)  {
        MyThread thread = new MyThread();
        thread.start();
    }
}

继承Thread类必须重写run()方法。然后通过start()方法去启动线程。注意,不是调用run()方法启动线程。调用run(),是在主线程中直接运行run方法

public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
} 
class MyRunnable implements Runnable{
    public MyRunnable() {
    }

    @Override
    public void run() {
        System.out.println("子线程ID:"+Thread.currentThread().getId());
    }
}

还可以通过实现Runnable接口来实现类似的功能。实现Runnable接口必须重写其run方法。然后通过Thread的start方法来创建一个新线程来执行该子任务。如果调用Runnable的run方法的话,是不会创建新线程的。
事实上,查看Thread类的实现源代码会发现Thread类是实现了Runnable接口的。

状态:

  • 创建(new)状态: 准备好了一个多线程的对象
  • 就绪(runnable)状态: 调用了start()方法, 等待CPU进行调度
  • 运行(running)状态: 执行run()方法
  • 阻塞(blocked)状态: 暂时停止执行, 可能将资源交给其它线程使用
  • 终止(dead)状态: 线程销毁

1.当需要新起一个线程来执行某个子任务时,就创建了一个线程。创建后,不会立即进入就绪状态,线程的运行需要一些条件(比如为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。
2.当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。
线程在运行状态过程中,可能有多个原因导致当前线程不继续运3.行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。
4.当由于突然中断或者子任务执行完毕,线程就会被消亡。

CPU的一个核在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换(对于进程也是类似)。为了被唤醒后能重新继续任务,线程上下文切换过程中会记录程序计数器、CPU寄存器状态等数据。

静态方法:
currentThread()方法
方法currentThread()可以返回代码段正在被哪个线程调用的信息。

public class Run1{
    public static void main(String[] args){                 
    System.out.println(Thread.currentThread().getName());
    }
}

sleep()方法
方法sleep()的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread()返回的线程。
sleep方法有两个重载版本:

sleep(long millis)     //参数为毫秒
sleep(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒

sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。
但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。

public class Test {

    private int i = 10;
    private Object object = new Object();

    public static void main(String[] args) throws IOException  {
        Test test = new Test();
        MyThread thread1 = test.new MyThread();
        thread1.start();
    } 


    class MyThread extends Thread{
        @Override
        public void run() {
            try {
                System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态");
                Thread.currentThread().sleep(10000);
            } catch (InterruptedException e) {
                // TODO: handle exception
            }
            System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束");
        }
    }
}

yield()方法
调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。

public class MyThread  extends Thread{
    @Override
    public void run() {
        long beginTime=System.currentTimeMillis();
        int count=0;
        for (int i=0;i<50000000;i++){
            count=count+(i+1);
            //Thread.yield();
        }
        long endTime=System.currentTimeMillis();
        System.out.println("用时:"+(endTime-beginTime)+" 毫秒!");
    }
}

public class Run {
    public static void main(String[] args) {
        MyThread t= new MyThread();
        t.start();
    }
}

sleep(),yield(),wait()区别:
1.sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会。yield()方法只会给相同优先级或更高优先级的线程以运行的机会。
2.与sleep不同,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,像一个新线程一样等待重新获取CPU执行时间。
3.wait()使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁,而sleep()不会导致锁行为的改变。调用wait()后, 需要别的线程执行notify/notifyAll才能够重新获得CPU执行时间。

实例方法:
start()方法
start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

run()方法
run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

getId()/getName()方法
getId()的作用是取得线程的唯一标识/名称

public class Test {
    public static void main(String[] args) {
        Thread t= Thread.currentThread();
        System.out.println(t.getName()+" "+t.getId());
    }
}

isAlive()方法
方法isAlive()的功能是判断当前线程是否处于活动状态,活动状态就是线程已经启动且尚未终止。

public class MyThread  extends Thread{
    @Override
    public void run() {
        System.out.println("run="+this.isAlive());
    }
}
public class RunTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread=new MyThread();
        System.out.println("begin =="+myThread.isAlive());
        myThread.start();
        System.out.println("end =="+myThread.isAlive());
    }
}

join()方法
在很多情况下,主线程创建并启动了线程,如果子线程中药进行大量耗时运算,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join()方法了。方法join()的作用是等待该线程终止的时间最长为 millis 毫秒。

public class Thread4 extends Thread{
    public Thread4(String name) {
        super(name);
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(getName() + "  " + i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        // 启动子进程
        new Thread4("new thread").start();
        for (int i = 0; i < 10; i++) {
            if (i == 5) {
                Thread4 th = new Thread4("joined thread");
                th.start();
                th.join();
            }
            System.out.println(Thread.currentThread().getName() + "  " + i);
        }
    }
}

interrupt()方法
停止一个线程可以使用Thread.stop()方法,但最好不用它。该方法是不安全的,已被弃用。
在Java中有以下3种方法可以终止正在运行的线程:

  • 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
  • 使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend及resume一样,都是作废过期的方法,使用他们可能产生不可预料的结果。
  • 使用interrupt方法(暂停线程),但这个不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。
while(!isInterrupted()){
……
}
//来判断线程是否被中断,这种情况下线程将直接退出。

图例:


参考:http://www.jianshu.com/p/40d4c7aebd66

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

推荐阅读更多精彩内容