Java多线程完整版基础知识

Java多线程完整版基础知识

(翟开顺由厚到薄系列)

1.前言

线程是现代操作系统中一个很重要的概念,多线程功能很强大,java语言对线程提供了很好的支持,我们可以使用java提供的thread类很容易的创建多个线程。线程很不难,我对之前学习过的基础,在这做了一个整理,本文主要参考的是Java研究组织出版的j2se进阶和张孝祥-java就业培训教材这两本书

2.概述

2.1线程是什么

主要是线程与进程的区别,这里不再阐述,自行网上搜索

为什么使用线程:操作系统切换多个线程要比调度进程在速度上快很多,进程间无法共享,通讯麻烦。线程之间由于共享数据,所以交换数据很方便

下面有个例子去解释多线程与单线程

A单线程例子


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package firstTread;

  

/**

 * @author zhaikaishun

 *

 */

public class TreadDemo1 {

    public static void main(String[] args) {

        new TestThread().run();  //会一直执行这段代码

        while(true){

            System.out.println("main thread is running");

        }

  

    }

  

}

class TestThread{  //这里没有继承Thread类

    public void run(){

        while (true){

        System.out.println(Thread.currentThread().getName()+" is here run");//会一直执行

        }

    }

}

运行后

分析:这里是单线程,会按照顺序,只会执行TestThread类的方法

B多线程例子


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package firstTread;

  

/**

 * @author zhaikaishun

 *

 */

public class TreadDemo1 {

    public static void main(String[] args) {

        new TestThread().run();  //会一直执行这段代码

        while(true){

            System.out.println("main thread is running");

        }

  

    }

  

}

class TestThread{  //这里没有继承Thread类

    public void run(){

        while (true){

        System.out.println(Thread.currentThread().getName()+" is here run");//会一直执行

        }

    }

}

结果:

分析:这里使用了多线程,TreadDemo1中的run方法
和main中的run方法会抢cpu执行,所以有时候输出有两种情况。注意,使用java多线程需要继承Thread类,还需调用其start()方法。

2.2java对线程的支持

Java吸收了一些多线程操作系统的技术特性,经过优化处理,在语言层次上实现了对线程的支持,它提供了Thread,Runnable,Thread,Group等一系列封装和类的接口,让程序员可以高效的开发java多线程程序,java还提供synchronized关键字和Object的wait(),notify()机制,用来实现进程的同步。

3.在java中使用线程

3.1Thread类和Runable方法

(a)继承Thread类

Java用Thread类对线程进行封装,一旦创建了这个Thread实例,jvm就会为我们创建一个线程,当我们调用Thread类的strat方法时,线程就开始运行起来。创建线程的方法如下

代码3.1,继承thread类创建线程的代码

我们也可以使用匿名类的办法创建线程,这样代码比较简洁但是可读性较差

代码3.2,匿名类继承Thread创建线程

(b)实现Runble接口

Runble是java提供的一个线程相关的接口,接口定义了一个方法

public void run();

某一个类一旦实现了该接口,那么这个类的实例就可以被一个java的thread对象调用。

代码3.3,自定义一个类,实现Runnable接口

代码3.4 匿名类实现Runnable接口

3.2两种线程实现方法的比较

不论是那种方式,最后都需要通过Thread类的实例调用start()方法来开始线程的执行,start()方法通过java虚拟机调用线程中定义的run方法来执行该线程。通过查看java源程序中的start()方法的定义可以看到,它是通过调用操作系统的start0方法来实现多线程的操作的。

但是一般在系统的开发中遇到多线程的情况的时候,以实现Runnable接口的方式为主要方式。这是因为实现接口的方式有很多的优点:

1、就是通过继承Thread类的方式时,线程类就无法继承其他的类来实现其他一些功能,实现接口的方式就没有这中限制;

2.也是最重要的一点就是,通过实现Runnable接口的方式可以达到资源共享的效果。

这个不举一个例子可能不太清楚,下面我就举一个买票的程序的例子

首先我们先写一个继承Thread类的程序,看看效果

首先是一个线程类,继承了

程序清单: ThreadTest类


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package firstTread;

 

/**

 * @author zhaikaishun

 *

 */

public class ThreadTest extends Thread {

     private int tickets = 100;

     public void run(){

         while (true){

             //模拟买票程序,每次调用这个方法,ticket就会减一张

             if(tickets>0)

                 System.out.println(Thread.currentThread().getName()+" is saling ticket "+tickets--);

         }

     }

}

程序清单:ThreadDemo4类


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package firstTread;

 

/**

 * @author zhaikaishun

 *

 */

public class ThreadDemo4 {

 

    public static void main(String[] args) {

     ThreadTest t=new ThreadTest();

     t.start();

     t.start();

     t.start();

     t.start();

    }

}

假如我们想用上述代码去模拟买票程序,run方法中每一次循环总票都减1,模拟卖出一张票,我们创建了一个线程,并且启动4次,希望能通过此种方式产生4个线程,结果怎么样呢

结果:从运行结果来看,我们发现只有一个线程在运行,无论我们启动多少遍start()方法,结果只有一个线程

接着我们修改ThreadDemo4类,在main方法中创建四个threadTest对象


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

package firstTread;

 

/**

 * @author zhaikaishun

 *

 */

public class ThreadDemo4 {

 

    public static void main(String[] args) {

     new ThreadTest().start();

     new ThreadTest().start();

     new ThreadTest().start();

     new ThreadTest().start();

    }

}

结果:确实是每个号被打了4遍,创建了4个线程,四个线程都在卖票,但是请注意,他们是各自在卖自己的100张票,并不能实现资源共享,不能去处理同一个资源

接着我们试着用实现Runable的方式,这才是正确的方式

程序清单:ThreadDemo5类


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package firstTread;

 

/**

 * @author zhaikaishun

 

 */

public class ThreadDemo5 {

 

    public static void main(String[] args) {

        ThreadTest t = new ThreadTest();  //这个类的实例就可以被一个java的thread对象调用。

        new Thread(t).start();//thread对象调用。

        new Thread(t).start();

        new Thread(t).start();

        new Thread(t).start();

    }

}


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package firstTread;

 

/**

 * @author zhaikaishun

 *

 */

public class ThreadTest implements Runnable { //现在是实现Runnable接口

     private int tickets = 100;

     public void run(){

         while (true){

             //模拟买票程序,每次调用这个方法,ticket就会减一张

             if(tickets>0)

                 System.out.println(Thread.currentThread().getName()+" is saling ticket "+tickets--);

         }

     }

}

结果:

如我们所想要的,四个线程同时处理一个资源

3.有关这两种方法的性能差异,现在的pc速度如此的快,我们认为在上面的前提下比较性能差异没有多大意义

我的建议:建议使用第二种方式,也就是实现Runable接口的方式

3.3线程的状态和属性

1. 新建状态(New):新创建了一个线程对象。

2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

注:Thread有个isAlive()方法,用来判断

4.线程同步

4.1线程安全问题:

在上面的卖票得例子中,有可能出现一种我们不想要的情况,那就是有可能同一张票被打印两次多多次,打印的票号码为0甚至是负数等

原因在这一段代码中

 if(tickets>0)

    System.out.println(Thread.currentThread().getName()+" is saling ticket "+tickets--);

假如当tickets=1的时候,

线程1刚刚判断完if(tickets>0),正要处理下面的语句的时候,cpu被线程2给抢走,线程2开始执行,当线程2执行完一个run方法,这里的tickets会减少1,这时候tickets为0,然后跳转到线程1的中断的地方继续执行,因为之前线程1判断过if(tickets>0),所以这里不再需要判断,直接执行System.out.println(Thread.currentThread().getName()+" is saling ticket "+tickets--);,将会打印出为0的票,也就意味着最后一张票卖了2次

4.2同步代码块

使用synchronized方法:保证代码块的内容是“原子的”(物理中原子的也是可以分割的,所以我一般不说原子),保证里面的内容只能被一个线程在执行,必须等执行的线程离开后才能让其他线程执行,也就和独木桥差不多。


1

2

3

synchronized (object) { 

            //这里写代码块

        }

独木桥会让我们的过桥效率降低,同样,同步代码块也会降低代码的执行速度,所以,如果确定代码是安全的,就不要使用同步代码块了。

同步代码块实现同步的原理:任何类型的对象都有一个标志位,该标志位具有0,1两种状态,其开始为1,当执行到synchrozied方法之后,object对象标识位变为0,另外一个线程执行到synchrozed方法之后,将会先判断这个状态,如果发现是0,就暂时阻塞。可以把这个标志位理解成一个箱子的锁,该箱子只能放一个人的东西。

上述卖票程序的ThreadTest类可以如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

package firstTread;

 

/**

 * @author zhaikaishun

 

 */

public class ThreadTest implements Runnable { // 现在是实现Runnable接口

    private int tickets = 100;

    String str = new String("");  //这里设置一个对象,任意一个对象都可以

    public void run() {

        while (true) {

            // 模拟买票程序,每次调用这个方法,ticket就会减一张

            synchronized (str) {

                // 这里写代码块

                if (tickets > 0) {

                    try {

                        Thread.sleep(10);

                    catch (Exception e) {

                        System.out.println(e.getMessage());

                    }

 

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

                            " is saling ticket " + tickets--);

                }

            }

        }

    }

}

结果:

注意: String str = new String("");
 这个标志对象,相当于监听对象,必须放在run方法的外面,如果放在run方法里面,四个线程每次调用run方法,就会产生4个监听对象,这四个同步监视器是4个不同的对象,会导致彼此之间不能同步。

4.3.同步函数

 
  上述是对代码块进行的同步,同样,我们也能对某一个方法进行同步,只需要在同步的函数前加上关键字synchronized即可

例如上述代码可以写成:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

package firstTread;

/**

 * @author zhaikaishun

 

 */

public class ThreadTest implements Runnable { // 现在是实现Runnable接口

    private int tickets = 100;

    String str = new String("");  //这里设置一个对象,任意一个对象都可以

    public void run() {

        while (true) {

            // 模拟买票程序,每次调用这个方法,ticket就会减一张

            sale();

        }

    }

    public  synchronized void sale(){  //同步方法

        if(tickets>0){

            try{

                Thread.sleep(10);

            }catch(Exception e){

                System.out.println(e.getMessage());

            }

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

                    " is saling ticket " + tickets--);

        }

    }

}

当有一个线程进入了synchronized方法(获得监视器),其他线程就不能进入通一个对象所有使用了synchronized修饰的方法,直到第一个对象执行完他所在的synchronized方法(离开监视器)。

思考:既然synchronized方法需要有一个标志位,那么同步方法的标志位是什么呢,这里我先给出答案,同步方法的标志对象就是所在的对象,即this。

4.4.代码块与函数间的同步

请看下面方法,通过一个str的取值,来判断是代码块还是函数间的同步

代码清单 ThreadDemo6


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

package firstTread;

 

/**

 * @author zhaikaishun

 

 */

public class ThreadDemo6 {

 

    public static void main(String[] args) {

         

        ThreadTest t = new ThreadTest(); 

        new Thread(t).start();//thread对象调用。

        //让线程暂停一会儿才直观

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

         

        t.str=new String("method");//如果str是method,调用同步函数

        new Thread(t).start();

         

    }

}

代码清单 ThreadTest


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

package firstTread;

 

/**

 * @author zhaikaishun

 

 */

public class ThreadTest implements Runnable { // 现在是实现Runnable接口

    private int tickets = 100;

    String str = new String(""); // 这里设置一个对象,任意一个对象都可以

 

    public void run() {

        if ("method".equals(str)) {

            while (true) {

                sale();

            }

        else {

            synchronized (str) {

                while (true) {

                    if (tickets > 0) {

                        try {

                            Thread.sleep(10);

                        catch (Exception e) {

                            System.out.println(e.getMessage());

                        }

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

                                " is saling ticket " + tickets--);

                    }

                }

            }

        }

    }

 

    public synchronized void sale() { // 同步方法

        if (tickets > 0) {

            try {

                Thread.sleep(10);

            catch (Exception e) {

                System.out.println(e.getMessage());

            }

            System.out.print("函数方法在执行:");

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

                    " is saling ticket " + tickets--);

        }

    }

}

运行结果:由于代码块和函数使用的监听器不一样,所以他们没有同步

如果想让他们同步,只需要设置相同的监听对象,将synchronized (str)改为synchronized (this)即可

5.死锁:

死锁比较少见,而且难于调试:

所谓死锁: 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

其实很久之前学习数字电路,经常会遇到一些锁,这也是自动化的一些常见的问题,在计算机中,也有类似的东西,请看下图

R1 和R2,都只能被一个进程使用

T1在使用R1,同时没有使用完R1的情况下,想使用R2

T2在使用R2,同时在没有使用完R2的情况下,想使用R1

这时,T1等待T2放弃使用R2,同时T2等待T1放弃使用R1,他们都不会放弃自己所使用的,于是产生了等待,将会一直僵持下去。

下面这个例子就是

线程1进去对象obj1的监视器,而线程2进入了obj2的监视器,这时候进入了obj1的监视器的线程还试图进入使用obj2作为监视器的方法中,这显然会被阻塞隔离,是进不去的;同时,进入了obj2的监视器的线程也试图进入使用obj1作为监视器的方法中,这也显然会被阻塞隔离,是进不去的。
然后双方一致僵持着,程序停滞不前,这就是我们所谓的死锁,代码清单如下

代码清单:死锁例子


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

package deadlock;

public class RunnableTest implements Runnable {  

    private int flag = 1;  

    private static Object obj1 = new Object(), obj2 = new Object();  

  

    public void run() {  

        System.out.println("flag=" + flag);  

        if (flag == 1) {  

            synchronized (obj1) {  

                System.out.println("我已经锁定obj1,休息0.5秒后锁定obj2去,但是估计进不去obj2,因为obj2也正在一个同步方法中");  

                try {  

                    Thread.sleep(500);  

                catch (InterruptedException e) {  

                    e.printStackTrace();  

                }  

                synchronized (obj2) {  

                    System.out.println("进入了obj2");
 

                }  

            }  

        }  

        if (flag == 0) {  

            synchronized (obj2) {  

                System.out.println("我已经锁定obj2,休息0.5秒后锁定obj1去,但是估计进不了obj1,因为这obj1也在一个同步方法中");
 

                try {  

                    Thread.sleep(500);  

                catch (InterruptedException e) {  

                    e.printStackTrace();  

                }  

                synchronized (obj1) {  

                    System.out.println("进入了obj1");
 

                }  

            }  

        }  

    }  

  

    public static void main(String[] args) {  

        RunnableTest run01 = new RunnableTest();  

        RunnableTest run02 = new RunnableTest();  

        run01.flag = 1;  

        run02.flag = 0;  

        Thread thread01 = new Thread(run01);  

        Thread thread02 = new Thread(run02);  

        System.out.println("线程开始喽!");  

        thread01.start();  

        thread02.start();  

    }  

}  

结果:一直处于僵持状态

6.线程间的通信

我们先从下面一个例子引出线程中的通信

下面例子讲的是一个生产和消费的关系,生产一样东西,取走这样东西。这个程序是每生产出一个PDD(人名),并且给这个人赋值为男

然后再取出来,然后生产一个“娇妹”(人名),并且赋值为女,然后再取出来。代码清单如下。

一个类Q,用来存储数据  Q:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

package communication;

public class Q {

  private String name="PDD";

  private String sex="男";

  public synchronized void put(String name,String sex) {

      this.name=name;

      try{Thread.sleep(1);}catch(Exception e){System.out.println(e.getMessage());}

      this.sex=sex; 

  }  

  public synchronized void get(){

      

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

  }

}

生产者类Producer:生产数据


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package communication;

public class Producer implements Runnable {

  Q q=null;

  public Producer(Q q){

      this.q=q;

  }

  int i=0;

  public void run(){

      while(true){

          if(i==0)

              q.put("PDD""男");

          else

              q.put("娇妹","女");

          i=(i+1)%2;

      }

  }

}

消费者类 Customer: 获取数据


1

2

3

4

5

6

7

8

9

10

11

12

13

14

package communication;

public class Customer implements Runnable {

    Q q=null;

    public Customer(Q q){

        this.q=q;

    }

    public void run(){

        while(true){

            q.get();

        }

    }

    

}

主方法:


1

2

3

4

5

6

7

8

9

10

11

12

package communication;

public class ThreadCommunication {

    public static void main(String[] args) {

        Q q = new Q();

        new Thread(new Producer(q)).start();

        try{Thread.sleep(1);}catch(Exception e){System.out.println(e.getMessage());}

        new Thread(new Customer(q)).start();

    }

}

运行结果:

.....

PDD----男

PDD----男

PDD----男

PDD----男

娇妹----女

娇妹----女

娇妹----女

娇妹----女

娇妹----女

娇妹----女

娇妹----女

.......

分析:这并不是我们想要的,我们想要的是下面这种类型的,producer每存放一次数据,customer取一次数据,反之,producer必须等customer取完数据之后才能开始存数据。这就是要将的线程间的通信问题,Java通过Object的wait,notify,notifyAll这几个方法实现线程间的通信。

PDD----男

娇妹----女

PDD----男

娇妹----女

PDD----男

娇妹----女

wait:告诉当前线程放弃监视器并且进入线程休眠状态,直到其他线程进入相同的监视器并且调用notify为止。

notify:唤醒同一对象监视器中调用wait的第一个线程。

notifyAll:唤醒同一对象监视器中调用wait的所有线程,具有优先级高的线程将会被先唤醒。

如果想让上面的程序满足我们的要求,我们可以在 类Q中定义一个新的成员变量bFull来标示数据存储空间的状态,当Customer取走数据后,bFull为false;当Producer存入数据后,bFull为true。只有bFull为true时,Customer才能取走数据,只有当bFull为False时Producer才能放入数据
Q的清单如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

package communication;

public class Q {

  private String name="PDD";

  private String sex="男";

  boolean bFull=false;

  public synchronized void put(String name,String sex) {

      if(bFull)

        try {

            wait();

        catch (InterruptedException e1) {

            // TODO Auto-generated catch block

            e1.printStackTrace();

        }

      this.name=name;

      try{Thread.sleep(1);}catch(Exception e){System.out.println(e.getMessage());}

      this.sex=sex; 

      bFull=true;

      notify();

  }  

  public synchronized void get(){

      if(!bFull)

            try {

                wait();

            catch (InterruptedException e1) {

                // TODO Auto-generated catch block

                e1.printStackTrace();

            }

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

      bFull=false;

      notify();

  }

}

结果:运行流程,自己看代码思考

娇妹----女

PDD----男

娇妹----女

PDD----男

娇妹----女

PDD----男

娇妹----女

参考文献:

【1】J2SE进阶(java研究组织 精品图书)

【2】张孝祥-Java就业教程

【3】java编程思想

【4】java核心技术卷1

时间: 07-11

Java多线程完整版基础知识的相关文章

JAVA多线程和并发基础面试问答【转】

JAVA多线程和并发基础面试问答 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题.(校对注:非常赞同这个观点) Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环境是一个包含了不同的类和程序的单 一进程.线程可以被称为轻量

Java中String的基础知识

Java中String的基础知识 ==与equal的区别 基本数据类型,指的是java中的八种基本数据结构(byte,short,char,int,long,float,double,boolean),一般的比较是使用的 ==,比较的是他们的值. 复合数据类型(类) ==比较的是两个对象的引用,可以理解为在内存中的地址,除非是同一个new出来的对象,他们的 ==为true,否则,都为false. equal是object中的方法.object中的实现如下,内部还是使用==实现,也就是说,如果一个

[基础] Java目录(摘自Java核心技术·卷1 基础知识)

Java核心技术·卷1 基础知识(原书第9版) 第1章 Java程序设计概述 1.1 Java程序设计平台 1.2 Java"白皮书"的关键术语 1.2.1 简单性 1.2.2 面向对象 1.2.3 网络技能 1.2.4 健壮性 1.2.5 安全性 1.2.6 体系结构中立 1.2.7 可移植性 1.2.8 解释型 1.2.9 高性能 1.2.10 多线程 1.2.11 动态性 1.3 Java applet与Internet 1.4 Java发展简史 1.5 关于Java的常见误解

17、JAVA多线程和并发基础面试问答

JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-answers/ 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题.(校对注:非常赞同这个观点) Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(se

JAVA多线程和并发基础面试问答(转载)

原文链接:http://www.cnblogs.com/dolphin0520/p/3932934.html 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题. Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环境是一个包含

JAVA多线程和并发基础面试问答

原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-answers/ 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题.(校对注:非常赞同这个观点) Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环

《Java核心技术 卷1 基础知识 原书第9版》pdf

下载地址:网盘下载 内容简介 编辑 CayS.Horstmann等编著,公飞编译的<Java核心技术>(CoreJava)自第1版出版以来,一直备受广大Java程序设计人员的青睐,畅销不衰,是Java经典书籍.第8版针对JavaSE6平台进行了全面更新,囊括了Java平台标准版(JavaSE/J2SE)的全部基础知识,提供了大量完整且具有实际意义的应用实例,详细介绍了Java语言基础知识.面向对象程序设计.接口与内部类.事件监听器模型.swing图形用户界面程序设计.打包应用程序.异常处理.登

Java面试题精粹(基础知识)

本篇博文是自己针对网络上已出现的Java面试题帖子汇总,汇总并不是给出链接或直接转载内容,而是分析对比并整理出各帖子中常见.高频的面试题,这些经常露脸的面试题透出面试的趋势(为什么可以成为经典的面试题)及面试官的共通点.而作为求职者.技术爱好者的我们不仅要能够自信.精准得回答这些问题,更要知道为什么出这些问题,面试官想考察我们什么或希望我们给出怎样的回答,适当的再对回答进行拓展以达到更好的效果.面试本身包含了一些心理上的东西,面试官对于问题是有一个心中期许的答案,面试者若能够触摸到边缘或击中要害

Java中浮点数的基础知识

偶然查看Math.round的JDK 1 public static int round(float a) { 2 if (a != 0x1.fffffep-2f) // greatest float value less than 0.5 3 return (int)floor(a + 0.5f); 4 else 5 return 0; 6 } 注释说0x1.fffffep-2f是最接近0.5的float类型的小数,咦,科学计数法用e表示指数我是知道的,但是这个p是什么鬼.可能有的读者还会问,