精华内容
下载资源
问答
  • Java线程总结

    2019-09-22 23:36:50
    Java线程总结 转载于:https://www.cnblogs.com/linliquan/p/11377290.html

     Java线程总结

     

     

     

     

    转载于:https://www.cnblogs.com/linliquan/p/11377290.html

    展开全文
  • Java 线程总结

    2010-12-21 16:12:23
    Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 ...Java线程:大总结
  • java线程总结

    2017-05-31 16:05:41
    14.6 总结 何时使用多线程技术,以及何时避免用它,这是我们需要掌握的重要课题。骼它的主要目的是对大量任务进行有序的管理。通过多个任务的混合使用,可以更有效地利用计算机资源,或者对用户来说显得更方便。...
    14.6 总结
    何时使用多线程技术,以及何时避免用它,这是我们需要掌握的重要课题。骼它的主要目的是对大量任务进行有序的管理。通过多个任务的混合使用,可以更有效地利用计算机资源,或者对用户来说显得更方便。资源均衡的经典问题是在IO等候期间如何利用CPU。至于用户方面的方便性,最经典的问题就是如何在一个长时间的下载过程中监视并灵敏地反应一个“停止”(stop)按钮的按下。
    多线程的主要缺点包括:
    (1) 等候使用共享资源时造成程序的运行速度变慢。
    (2) 对线程进行管理要求的额外CPU开销。
    (3) 复杂程度无意义的加大,比如用独立的线程来更新数组内每个元素的愚蠢主意。
    (4) 漫长的等待、浪费精力的资源竞争以及死锁等多线程症状。
    线程另一个优点是它们用“轻度”执行切换(100条指令的顺序)取代了“重度”进程场景切换(1000条指令)。由于一个进程内的所有线程共享相同的内存空间,所以“轻度”场景切换只改变程序的执行和本地变量。而在“重度”场景切换时,一个进程的改变要求必须完整地交换内存空间。
    线程处理看来好象进入了一个全新的领域,似乎要求我们学习一种全新的程序设计语言——或者至少学习一系列新的语言概念。由于大多数微机操作系统都提供了对线程的支持,所以程序设计语言或者库里也出现了对线程的扩展。不管在什么情况下,涉及线程的程序设计:
    (1) 刚开始会让人摸不着头脑,要求改换我们传统的编程思路;
    (2) 其他语言对线程的支持看来是类似的。所以一旦掌握了线程的概念,在其他环境也不会有太大的困难。尽管对线程的支持使Java语言的复杂程度多少有些增加,但请不要责怪Java。毕竟,利用线程可以做许多有益的事情。
    多个线程可能共享同一个资源(比如一个对象里的内存),这是运用线程时面临的最大的一个麻烦。必须保证多个线程不会同时试图读取和修改那个资源。这要求技巧性地运用synchronized(同步)关键字。它是一个有用的工具,但必须真正掌握它,因为假若操作不当,极易出现死锁。
    除此以外,运用线程时还要注意一个非常特殊的问题。由于根据Java的设计,它允许我们根据需要创建任意数量的线程——至少理论上如此(例如,假设为一项工程方面的有限元素分析创建数以百万的线程,这对Java来说并非实际)。然而,我们一般都要控制自己创建的线程数量的上限。因为在某些情况下,大量线程会将场面变得一团糟,所以工作都会几乎陷于停顿。临界点并不象对象那样可以达到几千个,而是在100以下。一般情况下,我们只创建少数几个关键线程,用它们解决某个特定的问题。这时数量的限制问题不大。但在较常规的一些设计中,这一限制确实会使我们感到束手束脚。
    大家要注意线程处理中一个不是十分直观的问题。由于采用了线程“调度”机制,所以通过在run()的主循环中插入对sleep()的调用,一般都可以使自己的程序运行得更快一些。这使它对编程技巧的要求非常高,特别是在更长的延迟似乎反而能提高性能的时候。当然,之所以会出现这种情况,是由于在正在运行的线程准备进入“休眠”状态之前,较短的延迟可能造成“sleep()结束”调度机制的中断。这便强迫调度机制将其中止,并于稍后重新启动,以便它能做完自己的事情,再进入休眠状态。必须多想一想,才能意识到事情真正的麻烦程度。
    本章遗漏的一件事情是一个动画例子,这是目前程序片最流行的一种应用。然而,Java JDK配套提供了解决这个问题的一整套方案(并可播放声音),大家可到java.sun.com的演示区域下载。此外,我们完全有理由相信未来版本的Java会提供更好的动画支持——尽管目前的Web涌现出了与传统方式完全不同的非Java、非程序化的许多动画方案。如果想系统学习Java动画的工作原理,可参考《Core Java——核心Java》一书,由Cornell&Horstmann编著,Prentice-Hall于1997年出版。若欲更深入地了解线程处理,请参考《Concurrent Programming in Java——Java中的并发编程》,由Doug Lea编著,Addison-Wiseley于1997年出版;或者《Java Threads——Java线程》,Oaks&Wong编著,O'Reilly于1997年出版。
    展开全文
  • Java线程总结

    2008-04-14 15:01:00
    版權申明,獲得授權轉載必須保留以下申明和鏈接:作者的blog:(http://blog.matrix.org.cn/page/Kaizen)在论坛上面常常看到初学者对线程的无可奈何,所以总结出了下面一篇文章,希望对一些正在学习使用java线程的初学者...
    版權申明,獲得授權轉載必須保留以下申明和鏈接:
    作者的blog:(http://blog.matrix.org.cn/page/Kaizen)

    在论坛上面常常看到初学者对线程的无可奈何,所以总结出了下面一篇文章,希望对一些正在学习使用java线程的初学者有所帮助。

    首先要理解线程首先需要了解一些基本的东西,我们现在所使用的大多数操作系统都属于多任务,分时操作系统。正是由于这种操作系统的出现才有了多线程这个概念。我们使用的windows,linux就属于此列。什么是分时操作系统呢,通俗一点与就是可以同一时间执行多个程序的操作系统,在自己的电脑上面,你是不是一边听歌,一边聊天还一边看网页呢?但实际上,并不上cpu在同时执行这些程序,cpu只是将时间切割为时间片,然后将时间片分配给这些程序,获得时间片的程序开始执行,不等执行完毕,下个程序又获得时间片开始执行,这样多个程序轮流执行一段时间,由于现在cpu的高速计算能力,给人的感觉就像是多个程序在同时执行一样。
    一般可以在同一时间内执行多个程序的操作系统都有进程的概念.一个进程就是一个执行中的程序,而每一个进程都有自己独立的一块内存空间,一组系统资源.在进程概念中,每一个进程的内部数据和状态都是完全独立的.因此可以想像创建并执行一个进程的系统开像是比较大的,所以线程出现了。在java中,程序通过流控制来执行程序流,程序中单个顺序的流控制称为线程,多线程则指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务.多线程意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行.(你可以将前面一句话的程序换成进程,进程是程序的一次执行过程,是系统运行程序的基本单位)

    线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制;但与进程不同的是,同类的多个线程是共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈.所以系统在产生一个线程,或者在各个线程之间切换时,负担要比进程小的多,正因如此,线程也被称为轻负荷进程(light-weight process).一个进程中可以包含多个线程.

    多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每个任务对应一个进程,同进程一样,一个线程也有从创建,运行到消亡的过程,称为线程的生命周期.用线程的状态(state)表明线程处在生命周期的哪个阶段.线程有创建,可运行,运行中,阻塞,死亡五中状态.通过线程的控制与调度可使线程在这几种状态间转化每个程序至少自动拥有一个线程,称为主线程.当程序加载到内存时,启动主线程.

    [线程的运行机制以及调度模型]
    java中多线程就是一个类或一个程序执行或管理多个线程执行任务的能力,每个线程可以独立于其他线程而独立运行,当然也可以和其他线程协同运行,一个类控制着它的所有线程,可以决定哪个线程得到优先级,哪个线程可以访问其他类的资源,哪个线程开始执行,哪个保持休眠状态。
    下面是线程的机制图:


    线程的状态表示线程正在进行的活动以及在此时间段内所能完成的任务.线程有创建,可运行,运行中,阻塞,死亡五中状态.一个具有生命的线程,总是处于这五种状态之一:
    1.创建状态
    使用new运算符创建一个线程后,该线程仅仅是一个空对象,系统没有分配资源,称该线程处于创建状态(new thread)
    2.可运行状态
    使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,使该线程处于可运行状态(Runnable)
    3.运行中状态
    Java运行系统通过调度选中一个Runnable的线程,使其占有CPU并转为运行中状态(Running).此时,系统真正执行线程的run()方法.
    4.阻塞状态
    一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态(Blocked)
    5.死亡状态
    线程结束后是死亡状态(Dead)

    同一时刻如果有多个线程处于可运行状态,则他们需要排队等待CPU资源.此时每个线程自动获得一个线程的优先级(priority),优先级的高低反映线程的重要或紧急程度.可运行状态的线程按优先级排队,线程调度依据优先级基础上的"先到先服务"原则.
    线程调度管理器负责线程排队和CPU在线程间的分配,并由线程调度算法进行调度.当线程调度管理器选种某个线程时,该线程获得CPU资源而进入运行状态.

    线程调度是先占式调度,即如果在当前线程执行过程中一个更高优先级的线程进入可运行状态,则这个线程立即被调度执行.先占式调度分为:独占式和分时方式.
    独占方式下,当前执行线程将一直执行下去,直 到执行完毕或由于某种原因主动放弃CPU,或CPU被一个更高优先级的线程抢占
    分时方式下,当前运行线程获得一个时间片,时间到时,即使没有执行完也要让出CPU,进入可运行状态,等待下一个时间片的调度.系统选中其他可运行状态的线程执行
    分时方式的系统使每个线程工作若干步,实现多线程同时运行

    另外请注意下面的线程调度规则(如果有不理解,不急,往下看):
    ①如果两个或是两个以上的线程都修改一个对象,那么把执行修改的方法定义为被同步的(Synchronized),如果对象更新影响到只读方法,那么只度方法也应该定义为同步的
    ②如果一个线程必须等待一个对象状态发生变化,那么它应该在对象内部等待,而不是在外部等待,它可以调用一个被同步的方法,并让这个方法调用wait()
    ③每当一个方法改变某个对象的状态的时候,它应该调用notifyAll()方法,这给等待队列的线程提供机会来看一看执行环境是否已发生改变
    ④记住wait(),notify(),notifyAll()方法属于Object类,而不是Thread类,仔细检查看是否每次执行wait()方法都有相应的notify()或notifyAll()方法,且它们作用与相同的对象 在java中每个类都有一个主线程,要执行一个程序,那么这个类当中一定要有main方法,这个man方法也就是java class中的主线程。你可以自己创建线程,有两种方法,一是继承Thread类,或是实现Runnable接口。一般情况下,最好避免继承,因为java中是单根继承,如果你选用继承,那么你的类就失去了弹性,当然也不能全然否定继承Thread,该方法编写简单,可以直接操作线程,适用于单重继承情况。至于选用那一种,具体情况具体分析。


    eg.继承Thread
    public class MyThread_1 extends Thread
    {
    public void run()
    {
    //some code
    }
    }


    eg.实现Runnable接口
    public class MyThread_2 implements Runnable
    {
    public void run()
    {
    //some code
    }
    }



    当使用继承创建线程,这样启动线程:
    new MyThread_1().start()


    当使用实现接口创建线程,这样启动线程:
    new Thread(new MyThread_2()).start()


    注意,其实是创建一个线程实例,并以实现了Runnable接口的类为参数传入这个实例,当执行这个线程的时候,MyThread_2中run里面的代码将被执行。
    下面是完成的例子:

    public class MyThread implements Runnable
    {

    public void run()
    {
    System.out.println("My Name is "+Thread.currentThread().getName());
    }
    public static void main(String[] args)
    {
    new Thread(new MyThread()).start();
    }
    }



    执行后将打印出:
    My Name is Thread-0

    你也可以创建多个线程,像下面这样
    new Thread(new MyThread()).start();
    new Thread(new MyThread()).start();
    new Thread(new MyThread()).start();



    那么会打印出:
    My Name is Thread-0
    My Name is Thread-1
    My Name is Thread-2


    看了上面的结果,你可能会认为线程的执行顺序是依次执行的,但是那只是一般情况,千万不要用以为是线程的执行机制;影响线程执行顺序的因素有几点:首先看看前面提到的优先级别


    public class MyThread implements Runnable
    {

    public void run()
    {
    System.out.println("My Name is "+Thread.currentThread().getName());
    }
    public static void main(String[] args)
    {
    Thread t1=new Thread(new MyThread());
    Thread t2=new Thread(new MyThread());
    Thread t3=new Thread(new MyThread());
    t2.setPriority(Thread.MAX_PRIORITY);//赋予最高优先级
    t1.start();
    t2.start();
    t3.start();
    }
    }


    再看看结果:
    My Name is Thread-1
    My Name is Thread-0
    My Name is Thread-2



    线程的优先级分为10级,分别用1到10的整数代表,默认情况是5。上面的t2.setPriority(Thread.MAX_PRIORITY)等价与t2.setPriority(10)
    然后是线程程序本身的设计,比如使用sleep,yield,join,wait等方法(详情请看JDKDocument)

    public class MyThread implements Runnable
    {
    public void run()
    {
    try
    {
    int sleepTime=(int)(Math.random()*100);//产生随机数字,
    Thread.currentThread().sleep(sleepTime);//让其休眠一定时间,时间又上面sleepTime决定
    //public static void sleep(long millis)throw InterruptedException (API)
    System.out.println(Thread.currentThread().getName()+" 睡了 "+sleepTime);
    }catch(InterruptedException ie)//由于线程在休眠可能被中断,所以调用sleep方法的时候需要捕捉异常
    {
    ie.printStackTrace();
    }
    }
    public static void main(String[] args)
    {
    Thread t1=new Thread(new MyThread());
    Thread t2=new Thread(new MyThread());
    Thread t3=new Thread(new MyThread());
    t1.start();
    t2.start();
    t3.start();
    }
    }


    执行后观察其输出:

    Thread-0 睡了 11
    Thread-2 睡了 48
    Thread-1 睡了 69




    上面的执行结果是随机的,再执行很可能出现不同的结果。由于上面我在run中添加了休眠语句,当线程休眠的时候就会让出cpu,cpu将会选择执行处于runnable状态中的其他线程,当然也可能出现这种情况,休眠的Thread立即进入了runnable状态,cpu再次执行它。
    [线程组概念]
    线程是可以被组织的,java中存在线程组的概念,每个线程都是一个线程组的成员,线程组把多个线程集成为一个对象,通过线程组可以同时对其中的多个线程进行操作,如启动一个线程组的所有线程等.Java的线程组由java.lang包中的Thread——Group类实现.
    ThreadGroup类用来管理一组线程,包括:线程的数目,线程间的关系,线程正在执行的操作,以及线程将要启动或终止时间等.线程组还可以包含线程组.在Java的应用程序中,最高层的线程组是名位main的线程组,在main中还可以加入线程或线程组,在mian的子线程组中也可以加入线程和线程组,形成线程组和线程之间的树状继承关系。像上面创建的线程都是属于main这个线程组的。
    借用上面的例子,main里面可以这样写:

    public static void main(String[] args)
    {
    /***************************************
    ThreadGroup(String name)
    ThreadGroup(ThreadGroup parent, String name)
    ***********************************/
    ThreadGroup group1=new ThreadGroup("group1");
    ThreadGroup group2=new ThreadGroup(group1,"group2");
    Thread t1=new Thread(group2,new MyThread());
    Thread t2=new Thread(group2,new MyThread());
    Thread t3=new Thread(group2,new MyThread());
    t1.start();
    t2.start();
    t3.start();
    }



    线程组的嵌套,t1,t2,t3被加入group2,group2加入group1。
    另外一个比较多就是关于线程同步方面的,试想这样一种情况,你有一笔存款在银行,你在一家银行为你的账户存款,而你的妻子在另一家银行从这个账户提款,现在你有1000块在你的账户里面。你存入了1000,但是由于另一方也在对这笔存款进行操作,人家开始执行的时候只看到账户里面原来的1000元,当你的妻子提款1000元后,你妻子所在的银行就认为你的账户里面没有钱了,而你所在的银行却认为你还有2000元。
    看看下面的例子:

    class BlankSaving //储蓄账户
    {
    private static int money=10000;
    public void add(int i)
    {
    money=money+i;
    System.out.println("Husband 向银行存入了 [¥"+i+"]");
    }
    public void get(int i)
    {
    money=money-i;
    System.out.println("Wife 向银行取走了 [¥"+i+"]");
    if(money<0)
    System.out.println("余额不足!");
    }
    public int showMoney()
    {
    return money;
    }
    }


    class Operater implements Runnable
    {
    String name;
    BlankSaving bs;
    public Operater(BlankSaving b,String s)
    {
    name=s;
    bs=b;



    }
    public static void oper(String name,BlankSaving bs)
    {



    if(name.equals("husband"))
    {
    try
    {
    for(int i=0;i<10;i++)
    {
    Thread.currentThread().sleep((int)(Math.random()*300));
    bs.add(1000);
    }
    }catch(InterruptedException e){}
    }else
    {
    try
    {



    for(int i=0;i<10;i++)
    {
    Thread.currentThread().sleep((int)(Math.random()*300));
    bs.get(1000);
    }
    }catch(InterruptedException e){}
    }
    }
    public void run()
    {
    oper(name,bs);
    }
    }
    public class BankTest
    {
    public static void main(String[] args)throws InterruptedException
    {
    BlankSaving bs=new BlankSaving();
    Operater o1=new Operater(bs,"husband");
    Operater o2=new Operater(bs,"wife");
    Thread t1=new Thread(o1);
    Thread t2=new Thread(o2);
    t1.start();
    t2.start();
    Thread.currentThread().sleep(500);
    }



    }



    下面是其中一次的执行结果:



    ---------first--------------
    Husband 向银行存入了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Husband 向银行存入了 [¥1000]


    看到了吗,这可不是正确的需求,在husband还没有结束操作的时候,wife就插了进来,这样很可能导致意外的结果。解决办法很简单,就是将对数据进行操作方法声明为synchronized,当方法被该关键字声明后,也就意味着,如果这个数据被加锁,只有一个对象得到这个数据的锁的时候该对象才能对这个数据进行操作。也就是当你存款的时候,这笔账户在其他地方是不能进行操作的,只有你存款完毕,银行管理人员将账户解锁,其他人才能对这个账户进行操作。
    修改public static void oper(String name,BlankSaving bs)为public static void oper(String name,BlankSaving bs),再看看结果:

    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Husband 向银行存入了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]
    Wife 向银行取走了 [¥1000]




    当丈夫完成操作后,妻子才开始执行操作,这样的话,对共享对象的操作就不会有问题了。
    [wait and notify]
    你可以利用这两个方法很好的控制线程的执行流程,当线程调用wait方法后,线程将被挂起,直到被另一线程唤醒(notify)或则是如果wait方法指定有时间得话,在没有被唤醒的情况下,指定时间时间过后也将自动被唤醒。但是要注意一定,被唤醒并不是指马上执行,而是从组塞状态变为可运行状态,其是否运行还要看cpu的调度。
    事例代码:

    class MyThread_1 extends Thread
    {
    Object lock;
    public MyThread_1(Object o)
    {
    lock=o;
    }
    public void run()
    {
    try
    {
    synchronized(lock)
    {
    System.out.println("Enter Thread_1 and wait");
    lock.wait();
    System.out.println("be notified");
    }
    }catch(InterruptedException e){}
    }
    }
    class MyThread_2 extends Thread
    {
    Object lock;
    public MyThread_2(Object o)
    {
    lock=o;
    }
    public void run()
    {
    synchronized(lock)
    {
    System.out.println("Enter Thread_2 and notify");
    lock.notify();
    }
    }
    }
    public class MyThread
    {
    public static void main(String[] args)
    {
    int[] in=new int[0];//notice
    MyThread_1 t1=new MyThread_1(in);
    MyThread_2 t2=new MyThread_2(in);
    t1.start();
    t2.start();
    }
    }




    执行结果如下:
    Enter Thread_1 and wait
    Enter Thread_2 and notify
    Thread_1 be notified


    可能你注意到了在使用wait and notify方法得时候我使用了synchronized块来包装这两个方法,这是由于调用这两个方法的时候线程必须获得锁,也就是上面代码中的lock[],如果你不用synchronized包装这两个方法的得话,又或则锁不一是同一把,比如在MyThread_2中synchronized(lock)改为synchronized(this),那么执行这个程序的时候将会抛出java.lang.IllegalMonitorStateException执行期异常。另外wait and notify方法是Object中的,并不在Thread这个类中。最后你可能注意到了这点:int[] in=new int[0];为什么不是创建new Object而是一个0长度的数组,那是因为在java中创建一个0长度的数组来充当锁更加高效。

    Thread作为java中一重要组成部分,当然还有很多地方需要更深刻的认识,上面只是对Thread的一些常识和易错问题做了一个简要的总结,若要真正的掌握java的线程,还需要自己多做总结  
    展开全文
  • 我总结的Java线程总结

    2008-12-13 08:30:17
    这是我总结整理的Java线程总结,希望对大家有所帮助!
  • Java线程总结(一)

    2021-06-08 22:02:18
    Java线程总结(一)一.线程的生命周期1)进程与线程的定义和特征2)线程的生命周期二.创建线程的三种方式1)继承Thread类2)实现Runnable接口3)实现Callable接口4)Callable与其他两种方式的区别三.CAS原理与ABA...

    一.线程的生命周期

    1)进程与线程的定义和特征

    1.进程的定义:进程是程序运行的一个实体的运行过程,是系统进行资源分配和调配的一个独立单位。
    2.进程的特征:2.1系统开销:创建撤销切换开销大,资源要重新分配和回收。
    2.2拥有资产:资源拥有的基本单位。2.3调度:资源分配的基本单位。2.4安全性:进程间相互独立,互不影响。2.5地址空间:系统赋予的独立的内存地址空间。
    3.线程的定义:线程是进程运行和执行的最小的调度单位。
    4.线程的特征:4.1系统开销:仅保存少量寄存器的内容,开销小,在进程的地址空间执行代码。4.2:拥有资产:基本不占资源,仅有不可少的资源(程序计数器,一组寄存器和栈)。4.3:调度:独立调度分配的单位。4.4:安全性:线程共享一个进程下面的资源,可以互相通信和影响。4.5:地址空间:由相关堆栈寄存器和线程控制表TCB组成,寄存器可被用来存储线程内的局部变量。

    2)线程的生命周期

    在这里插入图片描述
    1.线程的创建(三种方式):线程被new出来
    继承Thread类,,实现Runnable接口,,实现Callable接口。
    2.线程就绪:线程具有执行的资格,即线程调用了start(),没有执行的权利

    Thread t = new Thread();
        t.start();//线程就绪,准备启动
    

    3线程运行:线程具有执行的资格和具备执行的权利

    @Override
    	public void run() {
    		for (int i = 0; i <= 99; i++) {
    			System.out.println(currentThread().getName() + ">>>" + i);
    
    			try {
    				sleep(99);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    

    4.线程阻塞:没有执行的资格和执行的权利
    sieep()方法:他使得线程在指定的时间内进入阻塞状态,不能得到CPU时间,指定的时间一过,线程重新进入可执行状态

    /**
    	 * 阻塞---Sleep()
    	 * 
    	 * @param args
    	 */
    
    	public static void main(String[] args) {
    		Thread a1 = new SleepThread();
    		Thread a2 = new SleepThread();
    		Thread a3 = new SleepThread();
    		Thread a4 = new SleepThread();
    
    		a1.start();
    		a2.start();
    		a3.start();
    		a4.start();
    
    	}
    
    	public static class SleepThread extends Thread {
    		@Override
    		public void run() {
    			for (int i = 0; i <= 18; i++) {
    				System.out.println(currentThread().getName() + ">>>" + i);
    				try {
    					sleep(180); // 休息180ms
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    
    	}
    }
    
    

    yield()方法:使得线程放弃当前分得的CPU时间,但是不使线程阻塞,即线程仍处于可执行状态

    /**
     * 阻塞---yield
     * 
     * @author DELL
     *
     */
    
    public class ThreadYield {
    
    	public static void main(String[] args) {
    		Thread a1 = new YieldThread();
    		Thread a2 = new YieldThread();
    		Thread a3 = new YieldThread();
    		a1.start();
    		a2.start();
    		a3.start();
    	}
    
    	public static class YieldThread extends Thread {
    		@Override
    		public void run() {
    			for (int i = 0; i <= 18; i++) {
    				if (i == 9) {
    					Thread.yield();
    				}
    				System.out.println(currentThread().getName() + ">>>" + i);
    				try {
    					sleep(180);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    }
    

    join()方法:调用时当前线程则转入阻塞状态,直到另一个线程运行结束,当前线程再由阻塞状态转为就绪状态

    public class ThreadJoin {
    
    	public static void main(String[] args) throws Exception {
    		Thread a1 = new JoinThread();
    		Thread a2 = new JoinThread();
    		Thread a3 = new JoinThread();
    		a1.start();
    		a1.join(); // 等待a1跑完,再跑其他的
    		a2.start();
    		a3.start();
    	}
    
    	public static class JoinThread extends Thread {
    		@Override
    		public void run() {
    			for (int i = 0; i <= 18; i++) {
    				System.out.println(currentThread().getName() + ">>>" + i);
    				try {
    					sleep(180);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    }
    
    

    wait()方法:导致当前线程等待,直到其他线程调用此对象的notify()唤醒方法。
    5.线程死亡:线程的对象变成垃圾,释放资源

    二.创建线程的三种方式

    1)继承Thread类

    创建如下:

    public class ThreadDemo01 extends Thread {
    
    	/**
    	 * 线程创建方法1--继承Thread类
    	 */
    
    	@Override
    	public void run() {
    		for (int i = 0; i <= 99; i++) {
    			System.out.println(currentThread().getName() + ">>>" + i);
    
    			try {
    				sleep(99);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    }
    

    测试如下:

    public static void main(String[] args) {
    		Thread t1 = new ThreadDemo01();
    		Thread t2 = new ThreadDemo01();
    		Thread t3 = new ThreadDemo01();
    		Thread t4 = new ThreadDemo01();
    		Thread t5 = new ThreadDemo01();
    
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    		t5.start();
        
    	}
    

    2)实现Runnable接口

    创建如下:(也可用lambda表达式创建)

    public class ThreadDemo02 implements Runnable {
    
    	/**
    	 * Thread创建方法二 继承接口Runable
    	 */
    
    	@Override
    	public void run() {
    		for (int i = 0; i <= 99; i++) {
    			System.out.println(Thread.currentThread().getName() + ">>>" + i);
    			try {
    				Thread.sleep(99);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    }
    

    测试如下:

    public class TestThreadDemo02 {
    
    	public static void main(String[] args) {
    		Thread t1 = new Thread(new ThreadDemo02());
    		Thread t2 = new Thread(new ThreadDemo02());
    		Thread t3 = new Thread(new ThreadDemo02());
    		Thread t4 = new Thread(new ThreadDemo02());
    		Thread t5 = new Thread(new ThreadDemo02());
    
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    		t5.start();
    	}
    
    }
    

    3)实现Callable接口

    创建如下:

    public class ThreadDemo03 implements Callable<Integer> {
    
    	/**
    	 * Thread 第三种创建方法--继承与Callable接口----此方法适用于值的计算
    	 */
    
    	@Override
    	public Integer call() throws Exception {
    		int t = 0;
    		for (int i = 0; i < 99; i++) {
    			t += i;
    
    		}
    		return t;
    	}
    
    }
    

    测试如下:

    public class TestThreadDemo03 {
    
    	public static void main(String[] args) throws Exception {
    		FutureTask<Integer> ft = new FutureTask<Integer>(new ThreadDemo03());
    		Thread t = new Thread(ft);
    		t.start();
    		System.out.println(ft.get());
    	}
    
    }
        //输出:4851
    

    4)Callable与其他两种方式的区别

    1.call()方法可以有返回值
    2.call()方法可以声明抛出异常
    3.通常在做计算时使用
    代码展示如下:

    @Override
    	public Integer call() throws Exception { //可以直接抛出异常
    		int t = 0;
    		for (int i = 0; i < 99; i++) {
    			t += i;
    
    		}
    		return t;  //返回值
    	}
    
    

    三.CAS原理与ABA问题

    1.CAS原理:C–compare;A–And; S–Swap;比较并交换
    CAS有三个操作数:内存值(V), 预期原值(A), 新值(B);如果内存值与预期原值相同时,处理器自动将该位置的值(V)修改为新值(B),否则,处理器不做任何的操作。
    伪代码可以表示为:
    do{
    备份旧数据;
    基于旧数据构造新数据;
    }while(!CAS(内存地址,备份的旧数据,新数据))
    在这里插入图片描述
    2.ABA问题:CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了;比如链表的头部在变化了两次后恢复了原值,但是不代表链表就没有变化,,,所以java提供了:AtomicStampedReference/AtomicMarkableReference 来处理会发生ABA问题的场景,主要是在对象中额外在增加一个标记来标识对象是否有变更过。

    四.volatile和synchronized关键字

    1)volatile关键字

    volatile变量用来确保将变量的更新操作通知到其他线程;能在变量级别使用;能够实现变量修改的可见性,不能保证其原子性;在访问volatile变量时不会执行加锁操作,因此也就不会使执行线发生阻塞,,并且用它标记的变量不会被编译器优化。

    public class ThreadSecurity01 {
    	static volatile boolean flag = false; // 线程安全,实现了变量的可见性
    
    	public static void main(String[] args) throws Exception {
    		Thread a = new ThreadA();
    		Thread b = new ThreadB();
    
    		a.start();
    		Thread.sleep(1000);
    		b.start();
    	}
    
    	public static class ThreadA extends Thread {
    		@Override
    		public void run() {
    			while (true) {
    				if (flag) {
    					System.out.println("A>>" + flag);
    					try {
    						Thread.sleep(180);
    					} catch (InterruptedException e) {
    						e.printStackTrace();
    					}
    					break;
    				}
    
    			}
    		}
    	}
    
    	public static class ThreadB extends Thread {
    		@Override
    		public void run() {
    			flag = true;
    			System.out.println("B>>flag>>" + flag);
    			try {
    				Thread.sleep(180);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    

    2)synchronized关键字

    本质上时锁定当前变量,只有当前线程可以访问该变量,其他线程被堵塞住。
    使用级别:在变量,方法,和类级别的
    可以保证变量的修改可见性和原子性
    可能会造成线程的阻塞
    标记的变量可以被编译器优化

    synchronized时java中的关键字,是一种同步锁。它修饰的对象有以下几种:
    1.修饰代码块:被修饰的代码块称为同步语句块,范围是{}内的语句,作用的对象是调用这个代码块的对象。
    2.修饰一个方法,被修饰的方法称为同步方法,范围是整个方法,作用对象是调用这个方法的对象。
    3.修饰一个类,作用范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。
    4.修饰一个静态方法,作用范围是整个静态方法,作用的对象是这个类的所有对象。
    代码如下:

    public class ThreadSynchronized {
    
    	public static void main(String[] args) {
    		Ticket ticket = new Ticket();
    		TicketThread a1 = new TicketThread(ticket, "A");
    		TicketThread a2 = new TicketThread(ticket, "B");
    		TicketThread a3 = new TicketThread(ticket, "C");
    		TicketThread a4 = new TicketThread(ticket, "D");
    		TicketThread a5 = new TicketThread(ticket, "E");
    
    		a1.start();
    		a2.start();
    		a3.start();
    		a4.start();
    		a5.start();
    	}
    
    	/**
    	 * 解决方法一,synchronized锁定方法
    	 * 
    	 * @author DELL
    	 *
    	 */
    
    //	public static class Ticket {
    //		int num = 100;
    //                                     
    //		synchronized void sold(String name) { // 站点卖票  
    //			System.out.println(name + "卖出一张后,还余多少张:" + (--num));
    //		}
    //
    //		int getNum() {
    //			return this.num;
    //		}
    //	}
    
    	/**
    	 * 方法二:synchronized锁定语句块--
    	 * 
    	 * @author DELL
    	 *
    	 */
    
    	public static class Ticket {
    		int num = 100;
    
    		void sold(String name) {// 站点卖票
    			synchronized (this) {
    
    				System.out.println(name + "卖出一张后,还余多少张:" + (--num));
    			}
    		}
    
    		int getNum() {
    			return this.num;
    		}
    	}
    
    	public static class TicketThread extends Thread {
    		private Ticket ticket;
    		private String name;
    
    		public TicketThread(Ticket ticket, String name) {
    			this.ticket = ticket;
    			this.name = name;
    		}
    
    		@Override
    		public void run() {
    			while (ticket.getNum() > 0) {
    				ticket.sold(this.name);
    				try {
    					Thread.sleep(99);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    
    			}
    		}
    	}
    
    }
    

    五.AtomicInteger与int的区别

    1.AtomicInteger这个类是为了满足在高并发的情况下,原生的整数型值自增线程不安全问题,int是线程不安全的;用AtomicInteger可确保安全,,但是只能保证在自增或者自减的情况下保证线程安全

    public class ThreadSecurity02 {
    //	static  volatile int x = 0;   //此时线程不安全,只保证了可见性,++i,i++,,原子性不安全,,,,解决办法如下
    //
    //	public static void main(String[] args) {
    //		Thread a1 = new AtomicThread();
    //		Thread a2 = new AtomicThread();
    //		Thread a3 = new AtomicThread();
    //
    //		a1.start();
    //		a2.start();
    //		a3.start();
    //
    //	}
    //
    //	public static class AtomicThread extends Thread {
    //		@Override
    //		public void run() {
    //			for (int i = 0; i <= 100; i++) {
    //				System.out.println("X>>" + (++x));
    //				try {
    //					Thread.sleep(100);
    //				} catch (InterruptedException e) {
    //					e.printStackTrace();
    //				}
    //			}
    //		}
    //	}
    
    	static volatile AtomicInteger x = new AtomicInteger(0);
    
    	public static void main(String[] args) {
    		Thread a1 = new AtomicThread();
    		Thread a2 = new AtomicThread();
    		Thread a3 = new AtomicThread();
    
    		a1.start();
    		a2.start();
    		a3.start();
    
    	}
    
    	public static class AtomicThread extends Thread {
    		@Override
    		public void run() {
    			for (int i = 0; i <= 100; i++) {
    				System.out.println("X>>" + (x.incrementAndGet()));
    				try {
    					Thread.sleep(100);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    
    }
    
    

    六.线程的三大特性

    程序在运行时,如果不满足这三大特性,就有可能产生线程安全问题。

    1)原子性

    一个操作或者多个操作要么全部执行并且执行过程中不被任何因素打断,要么就不执行

    /**
     * 原子性实例
     * 
     * @author DELL
     *
     */
    
    public class ThreadSecurity02 {
    //	static  volatile int x = 0;   //++i,i++,,原子性不安全,,,,解决办法如下
    //
    //	public static void main(String[] args) {
    //		Thread a1 = new AtomicThread();
    //		Thread a2 = new AtomicThread();
    //		Thread a3 = new AtomicThread();
    //
    //		a1.start();
    //		a2.start();
    //		a3.start();
    //
    //	}
    //
    //	public static class AtomicThread extends Thread {
    //		@Override
    //		public void run() {
    //			for (int i = 0; i <= 100; i++) {
    //				System.out.println("X>>" + (++x));
    //				try {
    //					Thread.sleep(100);
    //				} catch (InterruptedException e) {
    //					e.printStackTrace();
    //				}
    //			}
    //		}
    //	}
    
    	static volatile AtomicInteger x = new AtomicInteger(0);
    
    	public static void main(String[] args) {
    		Thread a1 = new AtomicThread();
    		Thread a2 = new AtomicThread();
    		Thread a3 = new AtomicThread();
    
    		a1.start();
    		a2.start();
    		a3.start();
    
    	}
    
    	public static class AtomicThread extends Thread {
    		@Override
    		public void run() {
    			for (int i = 0; i <= 100; i++) {
    				System.out.println("X>>" + (x.incrementAndGet()));
    				try {
    					Thread.sleep(100);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}
    
    }
    

    类型引用可确保原子性安全,如下:
    在这里插入图片描述

    2)有序性

    在java内存模型中,允许编译器和处理器对指令进行重排序,但是排序过程中不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性

    public class ThreadYouXu {
    	public static class Vars {
    		static int i = 0, j = 0, k = 0; // 定义三个变量
    	}
    
    	public static void main(String[] args) {
    		Vars va = new Vars();
    
    		Thread a1 = new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				va.i = 1;
    				System.out.print("i>>" + va.i + "!!!");
    			}
    		});
    
    		Thread a2 = new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				va.j = 1;
    				System.out.print("j>>" + va.j + "!!!");
    			}
    		});
    		Thread a3 = new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				va.k = 1;
    				System.out.print("k>>" + va.k);
    			}
    		});
    
    		a1.start();
    		a2.start();
    		a3.start();
    
    	}
    	// 输出第一次结果:j>>1!!!i>>1!!!k>>1
    	// 输出第二次结果:j>>1!!!k>>1i>>1!!!
    	// 输出第三次结果:i>>1!!!j>>1!!!k>>1
    
    }
    
    

    3)可见性

    当多个线程同时访问一个变量时,一个线程修改了这个变量的值,其他线程能立即看得到它修改的值

    public class ThreadSecurity01 {
    	static volatile boolean flag = false; // 线程安全,实现了变量的可见性
    
    	public static void main(String[] args) throws Exception {
    		Thread a = new ThreadA();
    		Thread b = new ThreadB();
    
    		a.start();
    		Thread.sleep(1000);
    		b.start();
    	}
    
    	public static class ThreadA extends Thread {
    		@Override
    		public void run() {
    			while (true) {
    				if (flag) {
    					System.out.println("A>>" + flag);
    					try {
    						Thread.sleep(180);
    					} catch (InterruptedException e) {
    						e.printStackTrace();
    					}
    					break;
    				}
    
    			}
    		}
    	}
    
    	public static class ThreadB extends Thread {
    		@Override
    		public void run() {
    			flag = true;
    			System.out.println("B>>flag>>" + flag);
    			try {
    				Thread.sleep(180);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    
    	}
    }
    

    七.死锁的四个必要条件

    代码实例:

    /**
    	 * 死锁问题
    	 * 
    	 * @param args
    	 */
    
    	public static void main(String[] args) {
    		DeadLock a = new DeadLock("A");
    		DeadLock b = new DeadLock("B");
    		DeadLock c = new DeadLock("C");
    
    		Thread a1 = new DeadLockThread(a, b, c);
    		Thread a2 = new DeadLockThread(b, c, a);
    		Thread a3 = new DeadLockThread(c, a, b);
    
    		a1.start();
    		a2.start();
    		a3.start();
    	}
    
    	public static class DeadLock {
    		private String name;
    
    		public DeadLock(String name) {
    			this.name = name;
    		}
    
    		public String getName() {
    			return this.name;
    		}
    	}
    
    	public static class DeadLockThread extends Thread {
    		private DeadLock a;
    		private DeadLock b;
    		private DeadLock c;
    
    		public DeadLockThread(DeadLock a, DeadLock b, DeadLock c) {
    			this.a = a;
    			this.b = b;
    			this.c = c;
    		}
    
    		@Override
    		public void run() {
    			synchronized (a) {
    				System.out.println("锁住:" + a.getName() + "资源成功");
    				System.out.println("准备锁住:" + b.getName() + "资源成功");
    				synchronized (b) {
    					System.out.println("锁住:" + b.getName() + "资源成功");
    					System.out.println("准备锁住:" + c.getName() + "资源成功");
    					synchronized (c) {
    						System.out.println("锁住:" + c.getName() + "资源成功");
    					}
    					System.out.println("释放:" + c.getName() + "资源成功");
    				}
    				System.out.println("释放:" + b.getName() + "资源成功");
    			}
    			System.out.println("释放:" + a.getName() + "资源成功");
    		}
    
    	}
    
    }
    // 输出:锁住:A资源成功
             // 锁住:C资源成功
             //锁住:B资源成功
           //准备锁住:A资源成功
           //准备锁住:B资源成功
            //准备锁住:C资源成功
    
    

    1)互斥

    一个资源同时只能被一个线程所使用

    2)不可抢占

    进程以及获得的资源,没有外界力量来抢占它,资源自能够自动放弃

    3)请求与保持

    进程必须占有资源,再去申请,例如请求第二把锁的时候,需要保持自身的第一把锁不被释放

    4)循环等待

    两个线程时,是你等我释放锁,我等你释放锁,多个线程时,是头尾相接的等待----如同一个环形公路一样

    八.可重入锁

    可重入锁,也叫递归锁,值得是同一线程 外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不影响。 使用灵活,但必须要有释放锁的动作,且手动释放和开启锁,只能适用于代码块锁。
    Lock 是一个接口提供无条件、可轮训的、定时的、可中断的锁获取操作,所有加锁和解锁的方法都是显示的。
    ReentrantLock(可重入锁)是Lock接口的实现类。
    代码实例:

    /**
     * 可重入锁
     * 
     * @author DELL
     *
     */
    
    public class ReenTrantLockThread {
    	public static void main(String[] args) {
    		Ticket ticket = new Ticket();
    		TicketThread a1 = new TicketThread(ticket, "A");
    		TicketThread a2 = new TicketThread(ticket, "B");
    		TicketThread a3 = new TicketThread(ticket, "C");
    		TicketThread a4 = new TicketThread(ticket, "D");
    		TicketThread a5 = new TicketThread(ticket, "E");
    
    		a1.start();
    		a2.start();
    		a3.start();
    		a4.start();
    		a5.start();
    	}
    
    	public static class Ticket {
    		private final ReentrantLock lock = new ReentrantLock(); // 创建
    		int num = 100;
    
    		void sold(String name) { // 站点卖票
    			lock.lock(); // 表示要开始启动锁了
    			try {
    				System.out.println(name + "卖出一张后,还余多少张:" + (--num));
    			} finally {
    				lock.unlock(); // 处理
    			}
    		}
    
    		int getNum() {
    			return this.num;
    		}
    	}
    
    	public static class TicketThread extends Thread {
    		private Ticket ticket;
    		private String name;
    
    		public TicketThread(Ticket ticket, String name) {
    			this.ticket = ticket;
    			this.name = name;
    		}
    
    		@Override
    		public void run() {
    			while (ticket.getNum() > 0) {
    				ticket.sold(this.name);
    				try {
    					Thread.sleep(99);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    
    			}
    		}
    	}
    }
    
    
    展开全文
  • 1.线程的生命周期 2.同步方法
  • Java线程总结  线程的状态表示线程正在进行的活动以及在此时间段内所能完成的任务.线程有创建,可运行,运行中,阻塞,死亡五中状态.一个具有生命的线程,总是处于这五种状态之一: 1.创建状态 使用new运算符创建一个...
  • Java线程总结:   1.线程的概念 1.1先理解进程   一个任务就是一个程序,每个运行中的程序就是一个进程。 进程是表示资源分配的基本单位,又是调度运行的基本单位。例如,用户运行自己的程序,系统就创建一个进程...
  • java 线程总结(一)

    2018-03-09 10:40:33
    1 Java程序天生就是多线程程序 public class MultiThread{ ...// 获取Java线程管理MXBean ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); // 不需要获取同步的monitor和synchron...
  • java线程总结(一)

    2017-03-12 23:22:55
    这周有许多杂事干扰,导致自己没有提前做完作业,今后最好合理安排时间,不要任务压到最后;... java线程是个挺大的内容,网上对java总结也都非常完善,不过别人的总不是自己的,看过就容易忘记
  • Java线程总结(转)

    2016-04-11 18:22:48
    正是由于这种操作系统的出现才有了多线程这个概念。我们使用的windows,linux就属于此列。什么是分时操作系统呢,通俗一点与就是可以同一时间执行多个程序的操作系统,在自己的电脑上面,你是不是一边听歌
  • java 线程 总结(下)

    2012-05-21 13:59:35
    线程是可以被组织的,java中存在线程组的概念,每个线程都是一个线程组的成员,线程组把多个线程集成为一个对象,通过线程组可以同时对其中的多个线程进行操作,如启动一个线程组的所有线程等.Java线程组由java.lang...
  • 同步格言(摘录于Java核心技术:卷1):如果一个变量写入值,而这个变量接下类可能会被另一个线程读取,或者从另一个变量读值,而这个变量之前被另一个线程写入,必须用同步。 没有实现同步 两次运行的结果...
  • Java线程总结(1)

    2014-03-27 21:00:34
    1:线程VS进程 进程:处于运行中的程序,是系统资源进行资源分配和调度的独立单位:是系统级别的调用。 (PS:进程和程序的区别:程序是一个静态的指令集合,而进程是一个正在系统活动中的指令集合) 线程:程序...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,090
精华内容 8,036
关键字:

java线程总结

java 订阅