黑马程序员-------java基础------多线程

来源:互联网 发布:mac百度搜索历史 编辑:程序博客网 时间:2024/05/18 02:59
 ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

java基础知识-----多线程


引言

      理解线程,先要理解进程。

     1.进程

                一个正在执行的程序,每一个执行程序都有一个执行顺序又叫控制单元。

     2.线程

                 每一个控制单元就是线程。

     3.多线程

                   jvm启动时,有一个java.exe的进程,该进程中至少有一个线程执行。
                   而该线程执行的代码在main方法中--主线 程。 jvm中除了有主线程还有 一个负责垃圾回收的线程。
                  像这种在一个程序中有多个线程执行的方式称为多线程。   

     4.多线程的意义在哪? 

                 让线程产生同时运行的效果,提高运行效率。

     5.cpu运行原理

                1) 每一次运行效果不同源自其随机性。多个线程都在获取执行权,谁得到执行,谁就运行。运行多久,cpu决定。
                2)为什么会有同时执行效果?在同一时刻,只能有一个程序执行,之所以会有同时执行的效果是因为cpu在多个进 程之间做着快速的切换。

一、线程的创建

创建有两种方式

1.  继承Thread方式

    1)定义类集成Thread  -------class Demo extends Thread
    2)复写run方法----------------public void run()
    3)调用start方法开启线程-----demo d = new demo(); d.start();
        注意只能用start方法,因为调用start会自动找run方法需要被执行的代码。进行线程开启。
        而d.run()只是进行了run方法的调用。,不是开启线程。


//继承的方式创建线程class  Demo extends Thread{DemoName(String name){super(name);}        //复写方法public void run(){                //  此处放执行代码for(int i=1;i<40;i++){System.out.println(Thread.currentThread().getName()+"run"+i);}}}class Demo1{        public static void main(String[] args)       {            //创建线程            Demo d1 = new Demo();            Demo d2 = new Demo();           //开启线程,调用start方法          d1.start();          d2.start();       }}

2.实现Runnable接口

    1)定义类实现Runnable接口-------class demo implements Runnable
    2)覆盖Runnable中的run方法-------public void run()
    3)创建线程对象---------------------Thread t = new Thread();
    3) 创建Runnable接口子类对象-----demo d = new demo();
       作为参数传递给Thread的构造函数,见上步。
    4)调用start方法  -------------------t.satrt();

为什么将Runnabl子类对象作为参数进行传递?
因为因定义的run方法所属的对象时Runnable子类对象,所以要让线程去指定对象的run方法,
就必须明确该run方法所属的对象。


//用实现的方式创建线程class Demo implements Runnable{private int ticket = 20;Object obj = new Object();              //复写run方法public void run(){synchronized(obj)//进行同步处理{while(true){if(ticket>0){                                        //异常处理方式try{Thread.sleep(10);}catch (Exception e){                                                 System.out.println(e.toStrig());}System.out.println(Thread.currentThread().getName());}}}}}class Demo2{public static void main(String[] args) {Demo d = new Demo();                //创建线程Thread d1 = new Thread(d);Thread d2 = new Thread(d);Thread d3 = new Thread(d);                //线程启动,调用start()方法d1.start();d2.start();d3.start();}}


实现与继承的区别
       实现:线程代码写在Runnab接口子类的run方法中。
       继承:线程代码写在Thread子类run方法中
实现的好处
      避免了单继承的局限性。建议使用。java只有单继承,没有多继承。


二、线程运行状态

                                            run结束,消亡
                                                    |stop
       被创建--------------------->运行<--(notify、notifyAll)------(wait、sleep)------>冻结
                                                    |
                                            临时阻塞:具备执行资格,但没有执行权


1.线程运行中出现的安全问题

             多条语句操作一个线程共享数据时,一个线程执行了一部分,还没执行完,另一个线程就参与执行,导致共享数据的错误。

2.对于安全问题java提供的解决方案

            ---同步代码块
            synchronized(对象)
            {  
                   同步代码 
             }

3.解决的原理

         保证同步中只有一个线程进行。加锁     
         好处                                                                                    
                 解决了多线程的安全问题。
        弊端
                多个线程要判断锁,耗费资源
 -------------》加锁的前提:
                                           1)有2个或2个以上的线程
                                            2)多个线程要共用一个锁

4.扩展

        1) 同步的表现形式
            同步代码块
           Object obj = new Object();
          public void add(int n)
         {
              synchronized(obj)
             {
                 同步代码;
              }
           }

           2)同步函数
            public synchronized void add(int n)
            {
             }
           同步函数的锁是this,被对象调用,就有一个所属对象的引用
           当为静态同步函数时,锁是所属类对应的字节码文件  Class对象
          public static synchronized void add(int n)
          {
                synchronized(Ticjet.class)
               {
                      同步执行代码
               }
           }

          锁不是this,为什么?
          静态函数中不能定义this,静态static进入内存时,没有本类对象,但必有该类对应的字节码文件.class

5.死锁

          2个或者2个以上的进程在执行过程中,因为争夺资源而出现的一种相互等待的状态。
          若无外力作用,则无法进行下去,则称系统处于死锁状态,或者系统产生了死锁。
          当同步中嵌套同步时有可能出现死锁现象。
          要会写一个死锁程序
         
 
class Test  implements Runnable{private boolean flag;Test(boolean flag){this.flag = flag;}public void run(){if(flag){while(true){synchronized(MyLock.locka){System.out.println("----if locka");synchronized(MyLock.lockb){System.out.println("---if lockb");}}}}else{    while(true){synchronized(MyLock.lockb){System.out.println("---else lockb");synchronized(MyLock.locka){System.out.println("---else locka");}}}}}class MyLock{static  Object  locka = new Object();static Object lockb = new Object();}class  DeadLockTest{public static void main(String[] args) {Thread t1 = new Thread(new Test(true));Thread t2 = new Thread(new Test(false));t1.start();t2.start();}}

6.利用锁的单利设计模式

   1)饿汉式
      
 class Single        {            private finale Single s = new Single();           private Single(){}            public static Single  getInstance()           {                   return s;           }         }



    2)懒汉式-------加锁
      
class Single       {               private Single s = null;               private Single(){}               public static synchronized Single getInstance()              {                      if(s==null)                      {                            synchronized(Single.class)                             {                                     if(s==null)                                         s = new Single();                             }                      }                   return s;               }         }



三、线程间通信

1.线程间通信

             多个线程操作同一个资源,但操作动作不同。
             当某些代码需要被同事执行时,用单独的线程进行封装。


/*需求:买票程序多个窗口同时买票*/class Ticket implements Runnable //实现Runnable接口好处:避免单继承的局限性{private  int tick = 100;Object obj = new Object();//Object为所有类的子类,无需定义public void run(){                     //synchronized内放任意对象,synchronized(obj)//同步代码块,{}内放需要被同步的代码 {                //哪些被同步,就哪些操作共享数据while(true)   {    if(tick>0){try{Thread.sleep(10);}catch(Exception e ){}//此行代码可以显示出不安全数据System.out.println(Thread.currentThread().getName()+"sale :"+tick--);    }   }}}}class  maipiao2{public static void main(String[] args) {Ticket t = new Ticket();Thread t1 = new Thread(t);//实现Runnable语句  new Thread(对象p);Thread t2 = new Thread(t);Thread t3 = new Thread(t);Thread t4 = new Thread(t);                 t1.start();t2.start();t3.start();t4.start();}}




/*需求银行有一个金库有两个储户分别存300元,每次存100,存3次目的:该程序是否有安全问题,如果有,如何解决?如何找问题:1.明确哪些代码是多线程运行代码2.明确共享数据3.明确多线程运行代码中哪些语句是操作共享数据的*/class Bank{private int sum;public void add(int n){sum=sum+n;System.out.println("sum="+sum);}}class Cus implements Runnable{Bank b = new Bank();public void run(){for(int i=0;i<3;i++){b.add(100);}}}class    tongbuhanshu{public static void main(String[] args) {Cus c = new Cus();Thread t1 = new Thread(c);Thread t2 = new Thread(c);Thread t3 = new Thread(c);t1.start();t2.start();t3.start();}}



2.等待唤醒机制

             均使用在同步中,因为要对持有锁的线程操作,只有同步有锁。
             操作方法定义在Object中
             原因:
                      操作方法都在同步中,要使用这些方法必须要标示所属的同步锁。
                     而且只可被同一锁唤醒,锁又可以是任意对象,任意对象调用的方法
                     一定在Object方法中。

3.sleep与wait的区别

           wait():等待,释放执行权,释放锁
           sleep():释放执行权,不释放锁。

4.为什么定义notifyAll()

           因为在唤醒对方线程时,如果使用notify,容易出现只唤醒本线程的情况,
          导致程序中所有线程都等待。

5.jdk1.5后,停止线程的方法

1) 停止线程的一般方法
        一般情况,有一个
         run()
        {
             while(flag)
            {
                 执行代码
            }
         }
       只要在main方法,该线程执行一段时间,将flag进行标记为false,则线程就结束
2)特殊情况
            线程处于冻结状态,不会读取到标记,就不会结束,
            当没有指定的方式让冻结的线程恢复到正常运行状态当中时,
            就需要对冻结进行强制解除,强制恢复到正常运行状态。
           此时就可以读取标记,结束线程。------Thread    -----interrupt

6.扩展

          1)join方法
              临时加入线程执行,当A执行到B的join时,A等待,B进行执行。
              当B执行完后,A再进行执行。
          2)守护线程
               又叫后台线程
               Thread.setDaemon(true);线程启动之前
              启动后与前台线程共同抢cpu执行权,当所有前台都结束后,后台自动结束。
          3)setPriority(Thread.MAX_PRIORITY);
                设置线程优先级。
                MAX_PRIORITY   最高级10级
                MIN_PRIORITY    最低级 1级
                NORM_PRIORITY    默认优先级5级
              


                                                          ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------



0 0
原创粉丝点击