黑马程序员--多线程应用

来源:互联网 发布:数据接口大全 编辑:程序博客网 时间:2024/05/18 00:25

http://edu.csdn.net/heima android培训 http://edu.csdn.net/heima ; java培训期待与您交流!

进程:正在运行中的程序。

线程:就是进程中一个执行单元或执行情景或执行路径。负责进程中代码执行的控制单元。

多线程:一个进程中至少要有一个线程,当一个进程中有多个线程时,就是多线程。

多线程的好处:可以让多部分代码同时执行。

       什么是同时执行呢?   

              其实是cpu在瞬间做着快速的切换完成的。

其实java运行就是多线程的。

       在执行main函数中内容的同时,垃圾回收器也在回收堆内存的垃圾,所以执行main方法的线程,和执行垃圾回收器的线程同时在执行,这就是多线程。

创建多线程的目的:

       当有多部分代码需要同时执行时。

而且每一个线程都有自己要执行的内容,这个内容称之为:线程任务。

       简单说:启动多线程就是为了执行任务,当任务有多个,需要同时执行时,就需要多个

线程。

如何创建线程?

       Java要调用底层才能完成进程的建立和线程的创建,所以java对外提供了描述线程的对象,方便程序员对线程的操作。

Thread类:描述线程的类。

创建线程有两种方式:

创建线程的第一种方式:

              1、继承Thread类,覆盖run方法。

              自定义线程要运行的内容放在run方法中。

             run方法中可以调用其他方法。

       步骤:

              1、定义类继承Thread类。

              2、覆盖Thread类招工难的run方法。

              3、创建Thread类的子类对象创建线程对象。

              4、调用线程的start方法,开启线程。

              star()方法做了两件事:1、开启线程。2、调用run方法。

 

              画图:画单线程和多线程的运行图。

代码演示:

这是多线程的程序,小明和旺财的run()能同时交替执行。

publicclass ThreadDemo2 {

    publicstaticvoid main(String[] args) {

       Demo2 d1 = new Demo2("小明");

       Demo2 d2 = new Demo2("旺财");

//     d1.start(); //开启线程并执行run方法。

//     d2.start();

       d1.run();

       d2.run();

    }

 

}

class Demo2extends Thread {

    private Stringname;

    Demo2(String name){

       this.name = name;

    }

    //线程任务。

    @Override

    publicvoid run(){

       for(int x=1;x<=10;x++){

           for(int y=-9999999;y<99999999;y++){}

           System.out.println(name+"------"+x+":::::::"+getName());//返回当前线程的名字。

           System.out.println(name+"------"+x+":::::::"+currentThread().getName());//返回当前正在执行线程的名称。

       }

    }

}    

 

       Thread类中的方法:

             static Thread currentThread()返回对当前正在执行线程对象的引用。

              void start()开启线程,并调用run方法。

              Sleep(long millis):在指定的毫秒内让当前中在执行线程休眠。

注意:

       1、多个线程的运行是不规律的。

       2、必须得所有线程程序运行结束,进程才结束。

3、调用run方法和调用start方法的区别:

              调用run方法,仅仅是一般对象调用对象中的方法,并没有开启线程。

              调用start方法,开启一个线程,让这个线程去执行run方法中的内容。   

      4、当继承Thread时,因为创建了四个线程对象,每个线程都有一个num属性,而实现Runnable时,num数据是共享的,因为只创建了一个Ticket对象。

线程的状态:

       1、被创建。

       2、运行:这种状态的线程,具备着cpu的执行资格,具备着执行资格。

       3、冻结:这种状态的线程,释放了cpu执行资格,并释放了cpu执行权。相当于睡着了,但还会醒来。

                    有两种方法实现:

                            1sleep(time).唤醒sleep(time时间到)

                            2wait()睡着,唤醒notify()   wait()方法也可以指定时间,如果不指定                                    时间需要用notify()来唤醒。(是Object中的方法。)

       4、临时阻塞状态:这种状态的线程,具备着cpu执行权,不具备执行资格。

       5、消亡:线程结束了。通过stop()方法来完成。

             

需求:通过四个窗口卖票,一共有100张票。用第二种创建线程方式实现。

创建线程的第二种方式:

       1、定义一个类实现Runnable接口。

       2、覆盖Runnable接口中的run方法。

              将线程要运行的代码存储到run方法中。

       3、创建该接口的子类对象。

       4、通过Thread类进行线程的创建,并将Runnable接口的子类对象作为Thread的构造函数的实参进行传递。

       因为要明确运行要运行那个run方法。

       5、调用Thread类中的start方法开启线程。

实现Runnable接口的好处:

       1、通过Runnable接口可以降低线程对象和线程任务之间的耦合性。

              如果使用继承Thread类的方式,Thread类即封装了线程任务,又是线程对象。

       2、该方式避免了单继承的局限性。

              所以创建线程建议使用实现Runnable接口的方式。

同步:

              同步原理:其实就是将需要同步的代码进行封装,并在该代码上加一个锁。

              同步好处:解决了多线程安全问题。

              同步弊端:降低性能。

              (注意):加了同步,安全问题还在,如何解决?利用同步的两个前提来解决。

              同步的前提:

                    必须要保证在同步中有多个线程,因为同步中只有一个线程,该同步是没有意义的。

                    必须要保证多个线程在同步中使用的是同一个锁。

                           注意:当锁定义在局部中时,相当于每个线程都具备一个锁。这时就不是同一个锁了。

 

多线程安全问题:

 

多线程安全的原因:

1、  多个线程在操作共享数据。

2、  操作共享数据的代码有多条。    

一个线程在执行多条操作共享数据的过程中,其他线程参与了运算,这时就会发生安全问题。

分析多线程是否安全的依据:

线程中任务中有没有共享数据,该数据是否被多条语句操作。

安全问题解决方案:

只要保证一个线程在执行多条操作共享数据的语句时,其他线程不能参与运算即可。当该线程都执行完后,其他线程才可以执行这些语句。在多线程操作的代码上加上同步(synchronzied)

Synchornzied同步:

同步的原理:其实就是将需要同步的代码进行封装,并在该代码上加上一个锁。

同步的好处:解决多线程安全问题。

同步弊端:降低程序的运行效率,同时有可能会出现死锁情况。

一种现象:出现了多线程安全问题,为了解决,加上同步,发现问题依旧,怎么办?

同步的前提:

       必须要保证在同步中有多个线程,以为同步中只有一个线程的同步是没有意义的。

       必须要保证多个线程同步中使用的是同一锁。(将锁对象定义在成员位置)

多线程的程序,如何加同步?

1、  从线程任务代码中分析,也就是run方法中的代码中分析。

2、  分析啥呢?是否有共享数据,有没有多条语句在操作共享数据?

同步函数:将同步加在函数上。

如何将同步代码块以同步函数的形式表现?

如果run方法中的所有数据都要被同步,那么直接在run方法中加同步,如果只有部分代码需要同步,那么将需要被同步的代码用一个函数封装,然后在run方法中调用这个同步函数就行了。

同步函数和同步代码块有什么区别?

1、  同步函数使用的锁是this,同步代码块使用的锁是任意指定的对象。

建议开始时,使用同步代码块,因为锁可以是任意的。尤其是需要用到多个不同锁时。

同步函数使用的锁是什么?

       同步函数使用的锁是this

静态同步函数使用的锁是什么?

       静态随着类的加载而加载,这时内存中只存储的对象至少一个,就是该类字节码文件对象(类名.class)。所以静态同步函数使用的锁是字节码对象。

代码演示:

验证同步函数使用的锁是this

publicclass ThisLockDemo {

    publicstaticvoid main(String[] args) {

       /*

        * 通过两个线程来验证同步函数使用的锁是什么?

        */

       Ticket3 t = new Ticket3();

      

       Thread t1 = new Thread(t);

       Thread t2 = new Thread(t);

      

       t1.start();

       try{Thread.sleep(10);}catch(Exception e){} //停顿,以免main线程一下执行完。

       t.setFlag();

       t2.start();

    }

 

}

class Ticket3implements Runnable{

    privateintnum = 200;

//  private Object obj = new Object();

    privatebooleanflag =true;

    publicvoid run() {

       if(flag)

           while(true){

              synchronized(this){//使用obj锁和this锁的区别?

                  if(num>0){

                     try{Thread.sleep(10);}catch(Exception e){}

                      System.out.println(Thread.currentThread().getName()+"......code....."+num--);

                  }

              }

           }

       else

           while(true){

              show();

           }

    }

    //将标记改为false

    publicvoid setFlag(){

       flag =false;

    }

    publicsynchronizedvoid show() {//this

       if(num>0){

           System.out.println(Thread.currentThread().getName()+".......func...."+num--);

       }

    }

 

}

 

懒汉式的多线程处理:双重判断。

publicclass Single {

    privatestatic Singles = null;

    private Single(){}

    publicstatic Single getInstanse(){

       if(s==null){

           synchronized(Single.class){

              if(s==null)

                  s =new Single();

           }

       }

       returns;

    }

}

 

死锁:

最常见的死锁情况:同步嵌套。

同步中还有同步,两个同步用的不是同一个锁。

死锁代码演示:同步嵌套。

publicclass DeadLockTest {

    publicstaticvoid main(String[] args) {

       //TODO Auto-generated method stub

       Test a = new Test(true);//一个线程执行if

       Test b = new Test(false);//一个线程执行else

      

       Thread t1 = new Thread(a);

       Thread t2 = new Thread(b);

      

       t1.start();

       t2.start();

    }

 

}

class Testimplements Runnable{

    privatebooleanflag;

    Test(boolean flag){

       this.flag = flag;

    }

    publicvoid run(){

       if(flag){

           synchronized(MyLock.locka){

              System.out.println("if locka..........");

              synchronized(MyLock.lockb){

                  System.out.println("if lockb........");

              }

           }

       }

       else

           synchronized(MyLock.lockb){

              System.out.println("else lockb..........");

              synchronized(MyLock.locka){

                  System.out.println("else locka........");

              }

           }

    }

}

//该类提供了两个锁。

class MyLock{

    publicstatic Objectlocka =new Object();

    publicstatic Objectlockb =new Object();

}

 

线程间通信

线程间通信:多个线程在处理同一资源,但是处理的动作却不同。

代码演示:

线程间通信:有一堆资源,一个线程负责输入,一个线程负责输出

 

publicclass ResourceDemo {

    publicstaticvoid main(String[] args) {

       //1、创建资源对象。

       Resource r = new Resource();

       //2、创建线程任务。

       Input in = new Input(r);

       Output out = new Output(r);

       //3、创建线程对象。

       Thread t1 = new Thread(in);

       Thread t2 = new Thread(out);

       //4、开启线程。

       t1.start();

       t2.start();

    }

 

}

//描述资源。

class Resource{

    String name;

    String sex;

}

//描述输入任务。

class Inputimplements Runnable{

    private Resourcer;

    Input(Resource r){

       this.r = r;

    }

    publicvoid run(){

       int x = 0;//使用x做切换。还能用标记(truefals来做切换。)

       while(true){

           if(x==0){

              r.name ="Mike";

              r.sex ="man";

           }

           else{

              r.name ="小红";

              r.sex ="女女女女";

           }

           x = (x+1)%2; //x01切换,任何数模于2的值只能为01

       }

      

    }

}

//描述输出任务。

class Outputimplements Runnable{

    Resource r//保证资源的唯一。

    Output(Resource r){

       this.r = r;

    }

    publicvoid run(){

       while(true){

           System.out.println(r.name+"....."+r.sex);

       }

    }

}

http://edu.csdn.net/heima android培训 http://edu.csdn.net/heima ; java培训期待与您交流!详细请查看http://edu.csdn.net/heima

0 0