对于Java多线程的常见问题

Posted Cloudstrife

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对于Java多线程的常见问题相关的知识,希望对你有一定的参考价值。

最近在准备工作面试的问题,所以找了很多的资料,和自己整理了相关可能会考到的。每天争取发一篇。

1.多线程

实现方法:

一、继承Thread,重写run方法,调用start即可。

Class Thread1 extends Thread{

       Public void run(){

              //添加代码

}

}

Public static void main(String[] args){

       Thread1 st = new Thread1();

       St.start();

}

二、实现runnable接口,重写run方法,调用start。

Class RunThrad implements runnable{

       Public void run(){

              //添加代码

}

}

RunThread t = new RunThread();

Thread th = new Thread(t);

th.start();

三、实现Callable接口类,并重写call方法,使用FutureTask类来包装Callable实现类的对象,并且使用FutureTask对象来作为Thread对象的target来创建线程。

class  Mycallable  implements  Callable<Integer>{

       Public int call(){

              //添加代码

}

}

public static void main(String[] args){

       Mycallable<Integer> mycall = new Mycallable<Integer>();

       FutureTask<Integer> ft = new FutureTask<Integer>(mycall);

       Thread th = new Thread(ft);

       th.start();

}

实现runnable和callable比继承Thread方法的优势:

1、避免单继承的问题。

2、线程池只能够放入runnable和callable类的线程。

线程主要有五种状态,如下:

 

 

接下来介绍sleep,wait,notify,notifyAll,join,yield六种方法

对于wait,notify,notifyAll这三个,属于object类的方法,但必须搭配synchronized同步块来使用,即在synchronized修饰的同步代码块中或者方法中调用wait或者notify/notifyAll:

由于wait,notify/notifyAll是放在同步代码块中的,所以线程在执行的时候,肯定是进入了临界状态的,即该线程肯定是获得了锁的。

当执行wait方法时,会把当前的锁释放掉,让出cpu,进入等待状态。

当执行notify/notifyAll方法时,唤醒一个等待该对象锁的线程,然后继续往下执行,直到执行完synchronized的代码后,再将锁释放。(注意,notify/notifyAll执行后,并不立即释放锁,而是需要等待执行完synchronized的代码后)

如果在线程A,线程B中,在线程A中,执行wait,在线程B中执行notify,如果线程B先执行了notify,然后就结束了,然后线程A才去执行wait,那么此时线程A就无法被唤醒了。举例如下:有3个线程A,B,C。

class T implements Runnable{

       public String i;

       public String index;

       public T(String i,String index){

              this.i = i;

              this.index = index;

       }

       @Override

       public void run() {

              synchronized (index) {

                     while (true) {

                            try {

                                   index.notifyAll();

                                   System.out.println(i);

                                   index.wait();

                            } catch (InterruptedException e) {

                                   e.printStackTrace();

                            }

                     }

              }

       }

}

public class demo {

       public static void main(String[] args){

              String string1 = "s1";

              String string2 = "s2";

              String string3 = "s3";

              String index = "test";

              T a1 = new T(string1,index);

              T a2 = new T(string2,index);

              T a3 = new T(string3,index);

              Thread thread1 = new Thread(a1);

              Thread thread2 = new Thread(a2);

              Thread thread3 = new Thread(a3);

              thread1.start();

              thread2.start();

              thread3.start();

       }

}

输出结果:

s2

s1

s3

s1

s2

s1

s3

s1

……

 

多线程中测试某个条件的变化用 if 还是用 while?

以前一直不明白 当在线程的run()方法中需要测试某个条件时,为什么用while,而不用if,直到看到了这个简单的例子,终于明白了。。。。

这个例子是这样的:

有两个线程从List中删除数据,而只有一个线程向List中添加数据。初始时,List为空,只有往List中添加了数据之后,才能删除List中的数据。添加数据的线程向List添加完数据后,调用notifyAll(),唤醒了两个删除线程,但是它只添加了一个数据,而现在有两个唤醒的删除线程,这时怎么办??

如果用 if 测试List中的数据的个数,则会出现IndexOutofBoundException,越界异常。原因是,List中只有一个数据,第一个删除线程把数据删除后,第二个线程再去执行删除操作时,删除失败,从而抛出 IndexOutofBoundException。

但是如果用while 测试List中数据的个数,则不会出现越界异常!!!神奇。

当wait等待的条件发生变化时,会造成程序的逻辑混乱---即,List中没有数据了,再还是有线程去执行删除数据的操作。因此,需要用while循环来判断条件的变化,而不是用if。

 

 

两个线程之间的通信,可以采用如下两种方式,一种是基于while轮询,另外一种就是wait/notify的方式。

基于while轮询的,B线程一直  一直循环条件,当符合条件时,则抛出异常,结束线程。

基于wait/notify的方式,则使用共享变量。当A线程放弃CPU使用权,进入阻塞状态,则B线程获得共享变量,开始执行,执行完毕后,B线程结束,放弃CPU使用权,A线程继续。容易出现的问题是,A线程尚未执行,B线程先执行,打乱执行顺序。则逻辑错误。

参考案例在:http://www.cnblogs.com/hapjin/p/5492619.html

 

sleep:

sleep让当前线程休眠指定的时间。休眠完成后,状态转到就绪状态。

yield:  yield是放弃当前CPU资源,将CPU资源让给其他线程去使用,但放弃的时间不确定。

join:

大部分情况下,主线程启动了子线程,如果子线程需要完成大量复杂的运算,则主线程会先于子线程结束。但主线程如果需要在子线程运行完毕后使用子线程的结果,则必须在主线程中使用join,这样主线程会等待子线程结束,然后主线程再结束。

方法join,是使得所属线程对象x正常执行完run方法,而使当前线程z进行无限制阻塞,直到对象x结束后,再继续执行z后面的代码。

public class demo

{

   public static void main(String[] args) throws InterruptedException

   {

      Thread producer = new Producer();

      producer.start();

      producer.join();

      System.out.println("main end");

   }

}

class Producer extends Thread

{

   public void run()

   {

      for (int i = 0; i < 100000; i++)

      System.out.println("producer end");

         }

}

在以上案例中,当把producer.join()注释掉后,会先打印main end,再打印producer end。因为子线程运算复杂,所以主线程会先结束。而如果加上这一句,则主线程会等待producer线程的结束,主线程再销毁。也就是说,哪一个线程被调用,则必须等待该线程结束,调用的主线程才能继续执行下一步。

 

以上是关于对于Java多线程的常见问题的主要内容,如果未能解决你的问题,请参考以下文章

对于Java多线程的常见问题

Java中多线程,synchronized,与 AtomicInteger的问题

Java多线程实现的4中方式

探索并发编程------ Java多线程开发技巧

java 多线程和线程池

Java多线程之~~~ReadWriteLock 读写分离的多线程实现