黑马程序员_java多线程下

来源:互联网 发布:淘宝棉麻女装 编辑:程序博客网 时间:2024/06/05 13:26

------- android培训java培训java博客、java学习型技术博客、期待与您交流! ---------- 

 

同步函数使用的是哪一个锁呢?

函数需要被对象调用。那么函数有一个所属对象的引用。就是this

所以同步函数使用的锁是this

通过以下程序进行验证:

使用两个线程卖票。

一个线程在同步代码块中。

一个线程在同步函数中。

都在执行卖票动作。

class Ticketimplements Runnable//extends Thread

  {

    privateintpiao=100;//一般不用static修饰达到共享100张票,因为生命周期太长

    //Object obj=new Object();

    booleanblog=true;

    publicvoid run(){

        while(true){

            if(blog==true){

            synchronized(this){//同步代码块

                if(piao>0){

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

                  }

                 }

            }

            else{

                 show();

            }

        }

    }

    publicsynchronizedvoid show(){//同步函数

        if(piao>0){

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

          }

    }

}

publicclass ThreadDemo2 {

    publicstaticvoid main(String[] args) {

        Ticket tic=new Ticket();

        Thread t1=new Thread(tic);

        Thread t2=new Thread(tic);

        t1.start();

        tic.blog=false;

        t2.start();

    }

}

 

静态同步函数使用的锁是Class对象

如果同步函数被静态修饰后,使用的锁是什么呢?

通过验证发现,不再是this,因为静态方法中也不可以定义this

 

静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

类名.class  该对象的类型是Class

静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class

//单例模式,懒汉式

class Single{

   privatestatic Singles=null;

   private Single(){}

   publicstatic Single getIncetance(){

      if(s==null){

      synchronized (Single.class) {

          if(s==null)

             s=new Single();

      }

      }

      returns;

   }

}

 

多线程-----死锁

同步中嵌套同步

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{

    static Objectlocka=new Object();

    static Objectlockb=new Object();

}

publicclass BlockTest {

    publicstaticvoid main(String[] args) {

        Thread t1=new Thread(new Test(true));  

        Thread t2=new Thread(new Test(false)); 

        t1.start();t2.start();

    }

}

结果:if locka

     else lockb

 

6、多线程通信及线程安全

需求:

我们分别定义两个线程,实现Runnable;还有一个类是资源类,里面包含namesex

其中一个线程是Input,只管录入资源信息,而另外一个线程Out负责往外去出资源;

为了保证取出与放入是同一份资源,那么涉及到线程安全问题,我们想到了synchronized

锁机制,首先明确哪些是操作多线程的。存取为了保证同步,他们必须持有同种锁,而由代码可知,资源类Resdu对象是唯一的,因此他们的锁可以是 r

代码说明:

 

如何让资源共享呢1、利用static修饰,但是由于静态生命周期过长,我们一般不这么做;

                     2、单例模式,可以实现,但是又有点麻烦;不可取

                     3、于是我们想到利用构造函数进行初始化(对象),传入同一份资源(对象),

                        便可达到目的。

class Res{

    String name;

    String sex;

}

class Inputimplements Runnable{

    private Resr;

    Input(Res r){//对象初始化

    this.r=r;

    }

    publicvoid run(){

        int temp=0;

        while(true){

            synchronized(r){

            if(temp==0){

                        r.name="莉莉";

                        r.sex="女女";

            }else{

                  r.name="tom";

                  r.sex="男男男";

                }

            }

            temp=(temp+1)%2;

        }

    }

}

class Outimplements Runnable{

    private Resr;

    Out(Res r){

        this.r=r;

    }

    publicvoid run(){

        while(true){

            synchronized(r){

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

            }

        }

    }

}

publicclass Synchronized {

    publicstaticvoid main(String[] args) {

    Res res=new Res();

    Input in=new Input(res);

    Out ou=new Out(res);

    Thread t1=new Thread(in);

    Thread t2=new Thread(ou);

    t1.start();t2.start();

    }

}

结果:

莉莉----女女

莉莉----女女

莉莉----女女

tom----男男男

tom----男男男

.....

7、多线程通信--等待唤醒机制

需求:实现了存取功能,但是线程是不规律的,当某一线程拿到锁就会不断执行,最后才到下一个线程运行,我们如何让资源输入一个,就拿出一个呢?此时就用到了等待唤醒机制。 

wait(); notify(); notifiAll();

 

都使用在同步中,因为要对持有监视器(锁)的线程操作。

所以要使用在同步中,因为只有同步才具有锁。

为什么这些操作线程的方法要定义在Object类中呢?

因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,只有同一个锁上的被等待线程,才可以被同一个锁上的notify唤醒。不可以对不同锁中的线程进行唤醒。

也就是说,等待和唤醒必须是同一个锁。

而锁可以是任意对象,所以被任意对象调用的方法定义在Object类中。

 

class Res{

    String name;

    String sex;

    booleanflag=false;//标识

}

class Inputimplements Runnable{

    private Resr;

    Input(Res r){

    this.r=r;

    }

    publicvoid run(){

        int temp=0;

        while(true){

            synchronized(r){

                if(r.flag)

                   try{r.wait();}catch(Exception e){}//等待

            if(temp==0){

              r.name="莉莉";

              r.sex="女女";

            }else{

                  r.name="tom";

                  r.sex="男男男";

                }

            temp=(temp+1)%2;

            r.flag=true;

            r.notify();//唤醒

             }

        }

    }

}

class Outimplements Runnable{

    private Resr;

    Out(Res r){

        this.r=r;

    }

    publicvoid run(){

        while(true){

            synchronized(r){

                if(!r.flag)

                try{r.wait();}catch(Exception e){}

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

             r.flag=false;

             r.notify();

            }

        }

    }

}

publicclass Synchronized {

    publicstaticvoid main(String[] args) {

    Res res=new Res();

    Input in=new Input(res);

    Out ou=new Out(res);

    Thread t1=new Thread(in);

    Thread t2=new Thread(ou);

    t1.start();t2.start();

    }

}

结果:

莉莉----女女

tom----男男男

莉莉----女女

tom----男男男

莉莉----女女

.....

代码优化

class Res{

    private Stringname;

    private Stringsex;

    privatebooleanflag=false;

    publicsynchronizedvoid set(String name,String sex){//可能出现线程安全

        if(flag)

            try{this.wait();}catch(Exception e){}

        this.name=name;

        this.sex=sex;

        flag=true;

        this.notify();

    }

    publicsynchronizedvoid show(){

        synchronized(this){

            if(!flag)

                try{this.wait();}catch(Exception e){}

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

        flag=false;

        this.notify();

    }

 }

}

class Inputimplements Runnable{

    private Resr;

    Input(Res r){

    this.r=r;

    }

    publicvoid run(){

        int temp=0;

        while(true){      

            if(temp==0)

              r.set("莉莉","女女");

            else

              r.set("tom","男男男");

              temp=(temp+1)%2;

             }

        }

    }

class Outimplements Runnable{

    private Resr;

    Out(Res r){

        this.r=r;

    }

    publicvoid run(){

        while(true){

         r.show();

        }

    }

}

publicclass Synchronized {

    publicstaticvoid main(String[] args) {

        Res r=new Res();

        new Thread(new Input(r)).start();//匿名对象

        new Thread(new Out(r)).start();

    }

}

 

金典实例---生产者消费者

publicclass ProducerConsumerDemo {

    publicstaticvoid main(String[] args) {

       Resource r=new Resource();//资源

       Producer pro=new Producer(r);//生产者

       Consumer con=new Consumer(r);//消费者

       Thread t1=newThread(pro);

       Thread t2=newThread(con);

       Thread t3=newThread(pro);

       Thread t4=newThread(con);

       t1.start(); t2.start(); t3.start(); t4.start();

    }

}

class Resource{

    private Stringname;

    privateintcount=1;

    privatebooleanflag=false;

    publicsynchronizedvoid set(String name){

        while(flag)

            try{wait();}catch(Exception e){}

        this.name=name+"--"+count++;

        System.out.println(Thread.currentThread().getName()+"..生产者"+this.name);

        flag=true;

        this.notifyAll();

    }

    publicsynchronizedvoid out(){

        while(!flag)

            try{wait();}catch(Exception e){}

            System.out.println(Thread.currentThread().getName()+"..消费者---"+this.name);

            flag=false;

            this.notifyAll();

    }

}

class Producerimplements Runnable{//生产

    private Resourceres;

    Producer(Resource res){

        this.res=res;

    }

    publicvoid run(){

        while(true){

            res.set("+商品+");

        }

    }

}

class Consumerimplements Runnable{//消费

    private Resourceres;

    Consumer(Resource res){

        this.res=res;

    }

    publicvoid run(){

        while(true){

            res.out();

        }

    }

}

 

7、停止线程---interrupt

1、定义循环结束标记

   因为线程运行代码一般都是循环,只要控制了循环即可。

2、使用interrupt(中断)方法。

   该方法是结束线程的冻结作态,使线程回到运行作态中来。

注:stop方法已经过时,不在使用。 

Stop方法已经过时,如何停止线程?

只有一种,让run方法结束。

开启多个线程运行,运行代码通常是循环结构。

只要控制住循环,就可以让run方法结束,也就是线程结束。

 

特殊情况:当线程处于了冻结状态,就不会读到标记,那么线程就不会结束。

当没有指定让冻结的线程恢复到运行状态时,这时需要对冻结线程进行清除。

强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。

Thread类提供该方法 interrupt();

class StopDemoimplements Runnable{

    privatebooleanflag=true;

    publicsynchronizedvoid run(){

        while(flag){

            try{

                wait();

            }catch(InterruptedException e){

                System.out.println(Thread.currentThread().getName()+"异常了");

               flag=false;

            }

         System.out.println(Thread.currentThread().getName()+"run");

        }

    }

    publicvoid changeFlag(){

        flag=false;

    }

}

publicclass StopThread {

    publicstaticvoid main(String[] args) {

        int num=0;

        StopDemo sd=new StopDemo();

        Thread t1=new Thread(sd);

        Thread t2=new Thread(sd);

        t1.start();t2.start();

        while(true){

          if(num++==60){

              //sd.changeFlag();

             t1.interrupt();

             t2.interrupt();

              break;

          }

          System.out.println(Thread.currentThread().getName());

        }

     System.out.println("主线程---OVER");

    }

}

7、守护线程---setDaemon

setDaemon(boolean on):将该线程标记为守护线程或用户线程。

当正在运行的线程都是守护线程时,java虚拟机退出。

该方法必须在启动线程前调用。

随着主线程(前台线程)的结束,将线程中的无限循环结束,从而达到结束线程的目的,防止不被冻结。

8join方法

  A线程执行到了B线程的join()方法时,A就会等待。等B线程都执行完,A才会执行。

  Join可以用来临时加入线程执行。 

9、线程的优先级

1.线程中的toString()覆盖了父类Object中的方法,返回该线程的字符串形式,包括线程名称,优先级和线程组。

2yield() 暂停当前正在执行的线程对象,并执行其他线程。

    3、线程组:谁开启的线程,那么这个线程就归为哪个组。如主线程也是一个组;

4、类ThreadGroup(String name):构造一个新的线程组。

5优先级,代表抢夺资源的频率;

   setPriority(int newPriority):更改线程优先级。值可选1——10

  

public static final int ..

   MAX_PRIORITY:线程可以具有的最高优先级。--10

       MIN_PRIORITY:线程可以具有最低的优先级。--1

       NORM_PRIORITY:分配给默认的优先级。------5

 

6Runable r=new Runable(){

    Public void run(){

      for(int x=0;x<100;x++){

         System.out.ptint(Thread.currentThread().getName());

       }

    };  接口此处不能直接.start(),必须 new Thread(r).start();

  }

 

------- android培训java培训java博客、java学习型技术博客、期待与您交流! ---------- 

如有疑问:http://edu.csdn.net/

原创粉丝点击