zoukankan      html  css  js  c++  java
  • 对于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线程的结束,主线程再销毁。也就是说,哪一个线程被调用,则必须等待该线程结束,调用的主线程才能继续执行下一步。

  • 相关阅读:
    SQLITE3在php中的运用
    C# DllImport用法和路径问题
    ExtJs2.0学习系列(9)Ext.TabPanel之第一式
    ExtJs2.0学习系列(11)Ext.XTemplate
    ExtJs2.0学习系列(12)Ext.TreePanel之第一式
    ExtJs2.0学习系列(10)Ext.TabPanel之第二式
    ExtJs2.0学习系列(6)Ext.FormPanel之第三式(ComboBox篇)
    ExtJs2.0学习系列(15)extjs换肤
    ExtJs2.0学习系列(8)Ext.FormPanel之第五式(综合篇)
    ExtJs2.0学习系列(5)Ext.FormPanel之第二式
  • 原文地址:https://www.cnblogs.com/CloudStrife/p/7151315.html
Copyright © 2011-2022 走看看