作业day12——多线程

总结:

//多线程感觉挺难的,看了一下午,最后一个作业题还是没编出来,不知道哪儿错了。

又忘记了==和equals()的关系!(敲打)

1.   Java中多线程同步是什么?

在多线程程序下,同步能控制对共享资源的访问。如果没有同步,当一个Java线程在修改一个共享变量时,另外一个线程正在使用或者更新同一个变量,这样容易导致程序出现错误的结果。

2.   线程有几种?有哪些实现方法?

Java线程可以实现Runnable接口或者继承Thread类来实现,当你打算多重继承时,优先选择实现Runnable。

3.   Thread.start()与Thread.run()有什么区别?

Thread.start()方法(native)启动线程,使之进入就绪状态,当cpu分配时间该线程时,由JVM调度执行run()方法。

start() : 它的作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用。
run()   : run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程!

4.   为什么需要run()和start()方法,我们可以只用run()方法来完成任务吗?

我们需要run()&start()这两个方法是因为JVM创建一个单独的线程不同于普通方法的调用,所以这项工作由线程的start方法来完成,start由本地方法实现,需要显示地被调用,使用这俩个方法的另外一个好处是任何一个对象都可以作为线程运行,只要实现了Runnable接口,这就避免因继承了Thread类而造成的Java的多继承问题。

5.   Sleep()、suspend()和wait()之间有什么区别?

Thread.sleep()使当前线程在指定的时间处于“非运行”(NotRunnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了interrupt()方法,它将唤醒那个“睡眠的”线程。

注意:sleep()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep(),(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep(),也是当前线程进入睡眠,而不是t线程。t.suspend()是过时的方法,使用suspend()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend()容易引起死锁问题。

object.wait()使当前线程出于“不可运行”状态,和sleep()不同的是wait是object的方法而不是thread。调用object.wait()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用object.notify(),这样将唤醒原来等待中的线程,然后释放该锁。基本上wait()/notify()与sleep()/interrupt()类似,只是前者需要获取对象锁。

6.   当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗?

可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,Java没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。如果一个方法没有声明为同步,即使你在使用共享数据Java照样会调用,而不会做检查是否安全,所以在这种情况下要特别小心。一个方法是否声明为同步取决于临界区访问(critial section access),如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。

7.   在一个对象上两个线程可以调用两个不同的同步实例方法吗?

不能,因为一个对象已经同步了实例方法,线程获取了对象的对象锁。所以只有执行完该方法释放对象锁后才能执行其它同步方法。

8.    什么是死锁?

死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。就JavaAPI而言,线程死锁可能发生在一下情况。

●当两个线程相互调用Thread.join()

●当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。

9.   线程和进程有什么区别?

线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。

10. 什么是线程安全?Vector是一个线程安全类吗?

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可以将集合类分成两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。

11.Java中如何停止一个线程?

Java提供了很丰富的API但没有为停止线程提供API。JDK 1.0本来有一些像stop(),suspend() 和 resume()的控制方法但是由于潜在的死锁威胁因此在后续的JDK版本中他们被弃用了,之后Java API的设计者就没有提供一个兼容且线程安全的方法来停止一个线程。当run() 或者 call() 方法执行完的时候线程会自动结束,如果要手动结束一个线程,你可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程。

12. Java中notify 和 notifyAll有什么区别?

这又是一个刁钻的问题,因为多线程可以等待单监控锁,Java API 的设计人员提供了一些方法当等待条件改变的时候通知它们,但是这些方法没有完全实现。notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。

13.为什么wait和notify方法要在同步块中调用?

主要是因为Java API强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。还有一个原因是为了避免wait和notify之间产生竞态条件。

14. 什么是线程池? 为什么要使用它?

创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。

15. 如何写代码来解决生产者消费者问题?(多线程的应用问题,经典题目

在现实中你解决的许多线程问题都属于生产者消费者模型,就是一个线程生产任务供其它线程进行消费,你必须知道怎么进行线程间通信来解决这个问题。比较低级的办法是用wait和notify来解决这个问题,比较赞的办法是用Semaphore 或者 BlockingQueue来实现生产者消费者模型。

16. 如何避免死锁?

 Java多线程中的死锁
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件:

互斥条件:一个资源每次只能被一个进程使用。

请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。

17. Thread类中的yield方法有什么作用?

Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法而且只保证当前线程放弃CPU占用而不能保证使其它线程一定能占用CPU,执行yield()的线程有可能在进入到暂停状态后马上又被执行。

18.Java中你怎样唤醒一个阻塞的线程?

一但线程开始执行run方法,就会一直到这个run方法执行完成这个线程才退出。但在线程执行的过程中,可以通过两个方法使线程暂时停止执行。这两个方法是suspend和sleep。在使用suspend挂起线程后,可以通过resume方法唤醒线程。而使用sleep使线程休眠后,只能在设定的时间后使线程处于就绪状态(在线程休眠结束后,线程不一定会马上执行,只是进入了就绪状态,等待着系统进行调度)。

虽然suspend和resume可以很方便地使线程挂起和唤醒,但由于使用这两个方法可能会造成一些不可预料的事情发生,因此,这两个方法被标识为deprecated(弃用)标记,这表明在以后的jdk版本中这两个方法可能被删除,所以尽量不要使用这两个方法来操作线程。

 

 

 

 

编程题
(1)   写两个线程,一个线程打印 1~52,另一个线程打印字母A-Z。打印顺序为12A34B56C……5152Z。要求用线程间的通信。

 package com.jiale.demo01;

 

public class Printer {

    private int index = 1;

    public synchronized void print(int i){

       while(index % 3 == 0){

           try{

              wait();

           }catch(Exception e){}

       }

       System.out.print(i);

       index ++;

       notifyAll();

    }

    public synchronized void print(char c){

       while(index % 3 != 0){

           try{

              wait();

           }catch(Exception e){}

       }

       System.out.print(c);

       index ++;

       notifyAll();

    }

}

package com.jiale.demo01;

 

public class NumberPrinter extends Thread {

    private Printer p;

    public NumberPrinter(Printer p) {

       // TODO自动生成的构造函数存根

       this.p = p;

    }

    @Override

    public void run(){

       for(int i = 1; i <= 52; i ++){

           p.print(i);

       }

    }

}

package com.jiale.demo01;

 

public class LetterPrinter extends Thread {

    private Printer p;

    public LetterPrinter(Printer p) {

       // TODO自动生成的构造函数存根

       this.p = p;

    }

    @Override

    public void run() {

       for(char c = 'A'; c <= 'Z'; c ++){

           p.print(c);

       }

    }

}

package com.jiale.demo01;

 

public class Demo01 {

 

    public static void main(String[] args) {

       // TODO自动生成的方法存根

       Printer p = new Printer();

       Thread t1 = new NumberPrinter(p);

       Thread t2 = new LetterPrinter(p);

       t1.start();

       t2.start();

    }

 

}

运行结果:
12A34B56C78D910E1112F1314G1516H1718I1920J2122K2324L2526M2728N2930O3132P3334Q3536R3738S3940T4142U4344V4546W4748X4950Y5152Z
(2)   生产者消费者问题编程,用同步机制实现。
一个对象的同步方法被一个线程调用,另一个线程调用该对象的当前方法或其它同步方法时,只能等待,调用非同步方法不受限。  
注意事项:   
1、只有一个对象    2、有多个线程  
实现代码:   
1)类级的实现    
public classCommObject{     
   //同步方法:synchronized代表该方法加锁    
   public synchronized void a1(){   
        }     
   //非同步方法     
   public void a2(){    
   }  
}   
2)对象级的实现:类中的方法没有加synchronized关键,而是通过同步块说明同步。    
public classCommObject{     
   //同步方法     
   public void a1(){     
   }     
   //非同步方法    
   public void a2(){     
   }     
   public static void main(String[] args){
            CommObject com=newCommObject();                       synchronized(com){       
                     com.a1();       
                     com.a2();     
            }     
   }    
}
(3)  请编写一个类,类名为 Sub Thread,   Thread 类的子类。该类中定义了 含一个字符串参数的构造方法和 run()方法,方法中有一个 for循环,循环一共 进行5次,循环体先在命令行显示该线程循环了第几次,然后随机休眠小于一秒 的时间,循环结束后显示线程结束信息:线程名+finished  

  package com.jiale.demo02;

 

public class SubThread extends Thread {

    public SubThread(String str) {

       // TODO自动生成的构造函数存根

       super(str);

    }

    @Override

    public void run() {

       // TODO自动生成的方法存根

       for(int i = 1; i <= 5; i ++){

           System.out.println(getName()+"循环了:"+i+"次。");

           try{

              sleep((int)(Math.random()*1000));

           }catch(InterruptedException e){

           }

       }

       System.out.println(getName()+" Finished!");

    }

}

package com.jiale.demo02;

 

public class Demo02 {

 

    public static void main(String[] args) {

       // TODO自动生成的方法存根

       Thread  t1 = new  SubThread( "First" );

       Thread  t2 = new  SubThread( "Second" );

       Thread  t3 = new  SubThread( "Third" );

 

       t1.start( );

       t2.start( ); 

       t3.start( );

    }

 

}

运行结果:

Second循环了:1次。

First循环了:1次。

Third循环了:1次。

Second循环了:2次。

Second循环了:3次。

First循环了:2次。

Third循环了:2次。

Third循环了:3次。

First循环了:3次。

Second循环了:4次。

Third循环了:4次。

First循环了:4次。

Third循环了:5次。

Second循环了:5次。

Third Finished!

First循环了:5次。

Second Finished!

First Finished!

 
(4)  模拟3个人排除买票,张某、李某和赵某买电影票,售票员只有3张五元的钱,电影票5元一张。张某拿20元一张的RMB排在李某的前面,李某排在赵某的前面拿一张10元的RMB买票,赵某拿一张5元的RMB买票。

  package com.jiale.demo03;

 

public class TicketSeller {

    int money5=3, money10=0, money20=0; 

   

    public synchronized void rule(int money){ 

        if(money == 5){ 

            money5++; 

            System.out.println("给你入场券,你的钱正好。"); 

        } 

        else if(money == 20){ 

            while(money5 < 3){ 

                try

                    wait();

                } 

                catch(InterruptedException e){} 

            } 

            money5 = money5-3; 

            money20++; 

            System.out.println("给你入场券,你给我20元,找你15元。");

        } 

        else if(money == 10){ 

            while(money5 < 1){ 

                try

                    wait();

                } 

                catch(InterruptedException e){} 

            } 

            money5 = money5-1; 

            money10++; 

            System.out.println("给你入场券,你给我10元,找你5元。");

        } 

        notifyAll(); 

    } 

}

package com.jiale.demo03;

 

public class Buyer extends Thread {

    private TicketSeller ts;

    public Buyer(String str, TicketSeller ts) {

       // TODO自动生成的构造函数存根

       super(str);

       this.ts = ts;

    }

    public void setTs(TicketSeller ts) {

       this.ts = ts;

    }

    @Override

    public void run() {

       // TODO自动生成的方法存根

       if(getName() == "person1"

        { 

            ts.rule(20); 

        } 

        else if(getName() == "person2"

        { 

            ts.rule(10); 

        } 

        else if(getName() == "person3"

        { 

            ts.rule(5); 

        }

    }

 

}

package com.jiale.demo03;

 

public class Demo03 {

 

    public static void main(String[] args) {

       // TODO自动生成的方法存根

       TicketSeller ts = new TicketSeller();

       Buyer buyer1 = new Buyer("person1", ts);

       Buyer buyer2 = new Buyer("person2", ts);

       Buyer buyer3 = new Buyer("person3", ts);

       try{

           buyer1.start();

           buyer2.start();

           buyer3.start();

       }catch(Exception exp) {}

    }

 

}

 
 
 
Buyer类修改成了这样就好了:

package com.jiale.demo03;

 

public class Buyer extends Thread {

    private TicketSeller ts;

   

    public Buyer(String str, TicketSeller ts) {

       // TODO自动生成的构造函数存根

       super(str);

       this.ts = ts;

    }

 

    @Override

    public void run() {

       // TODO自动生成的方法存根

       if(getName().equals("person1")) 

        { 

            ts.rule(20); 

        } 

        else if(getName().equals("person2")) 

        { 

            ts.rule(10); 

        } 

        else if(getName().equals("person3")) 

        { 

            ts.rule(5); 

        }

    }

 

}

运行结果:

给你入场券,你给我20元,找你15元。

给你入场券,你的钱正好。

给你入场券,你给我10元,找你5元。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值