Java多线程深入理解

在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。

对于直接继承Thread的类来说,代码大致框架是:

?


1

2

3

4

5

6

7

8

9

10

11

class 类名 extends Thread{ 

方法1

方法2; 

… 

public void run(){ 

// other code… 

属性1; 

属性2; 

… 

先看一个简单的例子:

?


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

/**

 * @author Hashsound 继承Thread类,直接调用run方法

 * */

class hello extends Thread { 

  

    private String name; 

  

    public hello() { 

    

  

    public hello(String name) { 

        this.name = name; 

    

  

    public void run() { 

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

            System.out.println(name + "运行 " + i); 

        

    

  

    public static void main(String[] args) { 

        hello h1 = new hello("A"); 

        hello h2 = new hello("B"); 

        h1.run(); 

        h2.run(); 

    

【运行结果】:

A运行     0
A运行     1
A运行     2
A运行     3
A运行     4
B运行     0
B运行     1
B运行     2
B运行     3
B运行     4

我们会发现这些都是顺序执行的,说明我们的调用方法不对,应该调用的是start()方法。
当我们把上面的主函数修改为如下所示的时候:

?


1

2

3

4

5

6

public static void main(String[] args) { 

        hello h1=new hello("A"); 

        hello h2=new hello("B"); 

        h1.start(); 

        h2.start(); 

然后运行程序,输出的可能的结果如下:

B运行 0
B运行 1
B运行 2
A运行 0
A运行 1
A运行 2
B运行 3
B运行 4
A运行 3
A运行 4

因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的,呵呵。
注意:虽然我们在这里调用的是start()方法,但是实际上调用的还是run()方法的主体。
那么:为什么我们不能直接调用run()方法呢?
我的理解是:线程的运行需要本地操作系统的支持。
如果你查看start的源代码的时候,会发现:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public synchronized void start() { 

  

    /**

     * This method is not invoked for the main method thread or "system"

     * group threads created/set up by the VM. Any new functionality added 

     * to this method in the future may have to also be added to the VM.

     *

     * A zero status value corresponds to state "NEW".

     */

     if (threadStatus != 0 || this != me) 

            throw new IllegalThreadStateException(); 

        group.add(this); 

        start0(); 

        if (stopBeforeStart) { 

        stop0(throwableFromStop); 

    

private native void start0(); 

注意我用红色加粗的那一条语句,说明此处调用的是start0()。并且这个这个方法用了native关键字,次关键字表示调用本地操作系统的函数。因为多线程的实现需要本地操作系统的支持。
但是start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。
通过实现Runnable接口:
大致框架是:

来先看一个小例子吧:

?


1

2

3

4

5

6

7

8

9

10

11

class 类名 implements Runnable{ 

方法1

方法2; 

… 

public void run(){ 

// other code… 

属性1; 

属性2; 

… 

?


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

/**

 * @author Hashsound 实现Runnable接口

 * */

class hello implements Runnable { 

   

    public hello() { 

   

    

   

    public hello(String name) { 

        this.name = name; 

    

   

    public void run() { 

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

            System.out.println(name + "运行     " + i); 

        

    

   

    public static void main(String[] args) { 

        hello h1=new hello("线程A"); 

        Thread demo= new Thread(h1); 

        hello h2=new hello("线程B"); 

        Thread demo1=new Thread(h2); 

        demo.start(); 

        demo1.start(); 

    

    private String name; 

}

【可能的运行结果】:

线程A运行     0
线程B运行    0
线程B运行    1
线程B运行    2
线程B运行    3
线程B运行    4
线程A运行     1
线程A运行     2
线程A运行     3
线程A运行     4

关于选择继承Thread还是实现Runnable接口?
其实Thread也是实现Runnable接口的:

?


1

2

3

4

5

6

7

8

class Thread implements Runnable { 

    //… 

public void run() { 

        if (target != null) { 

             target.run(); 

        

     

其实Thread中的run方法调用的是Runnable接口的run方法。不知道大家发现没有,Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。

Thread和Runnable的区别:

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

/**

 * @author Rollen-Holt 继承Thread类,不能资源共享

 * */

class hello extends Thread { 

    public void run() { 

        for (int i = 0; i < 7; i++) { 

            if (count > 0) { 

                System.out.println("count= " + count--); 

            

        

    

   

    public static void main(String[] args) { 

        hello h1 = new hello(); 

        hello h2 = new hello(); 

        hello h3 = new hello(); 

        h1.start(); 

        h2.start(); 

        h3.start(); 

    

    private int count = 5

【运行结果】:

count= 5
count= 4
count= 3
count= 2
count= 1
count= 5
count= 4
count= 3
count= 2
count= 1
count= 5
count= 4
count= 3
count= 2
count= 1

大家可以想象,如果这个是一个买票系统的话,如果count表示的是车票的数量的话,说明并没有实现资源的共享。

我们换为Runnable接口

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

class MyThread implements Runnable{ 

   

    private int ticket = 5//5张票 

   

    public void run() { 

        for (int i=0; i<=20; i++) { 

            if (this.ticket > 0) { 

                System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--); 

            

        

    

public class lzwCode { 

       

    public static void main(String [] args) { 

        MyThread my = new MyThread(); 

        new Thread(my, "1号窗口").start(); 

        new Thread(my, "2号窗口").start(); 

        new Thread(my, "3号窗口").start(); 

    

【运行结果】:

count= 5
count= 4
count= 3
count= 2
count= 1

总结一下吧:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
所以,本人建议大家尽量实现接口。

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/**

 * @author Hashsound

 * 取得线程的名称

 * */

class hello implements Runnable { 

    public void run() { 

        for (int i = 0; i < 3; i++) { 

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

        

    

   

    public static void main(String[] args) { 

        hello he = new hello(); 

        new Thread(he,"A").start(); 

        new Thread(he,"B").start(); 

        new Thread(he).start(); 

    

【运行结果】:

A
A
A
B
B
B
Thread-0
Thread-0
Thread-0

说明如果我们没有指定名字的话,系统自动提供名字。
提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。

判断线程是否启动

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/**

 * @author Hashsound 判断线程是否启动

 * */

class hello implements Runnable { 

    public void run() { 

        for (int i = 0; i < 3; i++) { 

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

        

    

   

    public static void main(String[] args) { 

        hello he = new hello(); 

        Thread demo = new Thread(he); 

        System.out.println("线程启动之前---》" + demo.isAlive()); 

        demo.start(); 

        System.out.println("线程启动之后---》" + demo.isAlive()); 

    

【运行结果】

线程启动之前---》false
线程启动之后---》true
Thread-0
Thread-0
Thread-0

主线程也有可能在子线程结束之前结束。并且子线程不受影响,不会因为主线程的结束而结束。

线程的强制执行:

?


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

/**

 * @author Hashsound 线程的强制执行

 * */

class hello implements Runnable { 

    public void run() { 

        for (int i = 0; i < 3; i++) { 

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

        

    

   

    public static void main(String[] args) { 

        hello he = new hello(); 

        Thread demo = new Thread(he,"线程"); 

        demo.start(); 

        for(int i=0;i<50;++i){ 

            if(i>10){ 

                try

                    demo.join();  //强制执行demo 

                }catch (Exception e) { 

                    e.printStackTrace(); 

                

            

            System.out.println("main 线程执行-->"+i); 

        

    

【运行的结果】:

main 线程执行-->0
main 线程执行-->1
main 线程执行-->2
main 线程执行-->3
main 线程执行-->4
main 线程执行-->5
main 线程执行-->6
main 线程执行-->7
main 线程执行-->8
main 线程执行-->9
main 线程执行-->10
线程
线程
线程
main 线程执行-->11
main 线程执行-->12
main 线程执行-->13
...

线程的休眠:

/**
 * @author Hashsound 线程的休眠
 * */
class hello implements Runnable {
    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + i);
        }
    }  

    public static void main(String[] args) {
        hello he = new hello();
        Thread demo = new Thread(he, "线程");
        demo.start();
    }
}  

【运行结果】:(结果每隔2s输出一个)

线程0
线程1
线程2

线程的中断:

/**
 * @author Hashsound 线程的中断
 * */
class hello implements Runnable {
    public void run() {
        System.out.println("执行run方法");
        try {
            Thread.sleep(10000);
            System.out.println("线程完成休眠");
        } catch (Exception e) {
            System.out.println("休眠被打断");
            return;  //返回到程序的调用处
        }
        System.out.println("线程正常终止");
    }  

    public static void main(String[] args) {
        hello he = new hello();
        Thread demo = new Thread(he, "线程");
        demo.start();
        try{
            Thread.sleep(2000);
        }catch (Exception e) {
            e.printStackTrace();
        }
        demo.interrupt(); //2s后中断线程
    }
}  

【运行结果】:

执行run方法
休眠被打断

在java程序中,只要前台有一个线程在运行,整个java程序进程不会小时,所以此时可以设置一个后台线程,这样即使java进程小时了,此后台线程依然能够继续运行。

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/**

 * @author Hashsound 后台线程

 * */

class hello implements Runnable { 

    public void run() { 

        while (true) { 

            System.out.println(Thread.currentThread().getName() + "在运行"); 

        

    

   

    public static void main(String[] args) { 

        hello he = new hello(); 

        Thread demo = new Thread(he, "线程"); 

        demo.setDaemon(true); 

        demo.start(); 

    

虽然有一个死循环,但是程序还是可以执行完的。因为在死循环中的线程操作已经设置为后台运行了。

线程的优先级:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

/**

 * @author Hashsound 线程的优先级

 * */

class hello implements Runnable { 

    public void run() { 

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

            System.out.println(Thread.currentThread().getName()+"运行"+i); 

        

    

   

    public static void main(String[] args) { 

        Thread h1=new Thread(new hello(),"A"); 

        Thread h2=new Thread(new hello(),"B"); 

        Thread h3=new Thread(new hello(),"C"); 

        h1.setPriority(8); 

        h2.setPriority(2); 

        h3.setPriority(6); 

        h1.start(); 

        h2.start(); 

        h3.start(); 

    

【运行结果】:

A运行0
A运行1
A运行2
A运行3
A运行4
B运行0
C运行0
C运行1
C运行2
C运行3
C运行4
B运行1
B运行2
B运行3
B运行4

但是请读者不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源、

另外,主线程的优先级是5.

线程的礼让。

在线程操作中,也可以使用yield()方法,将一个线程的操作暂时交给其他线程执行。

原文地址:https://www.cnblogs.com/chenshengjava/p/8776296.html

时间: 2024-11-07 16:45:52

Java多线程深入理解的相关文章

有关JAVA多线程的理解

不同于c++等语言的调用操作系统的线程调控机制来实现多线程,java语言内置了多线程的api包,因此可以更加方便的使用多线程技术.(1)线程的问题.进程是程序的一次动态执行过程,它对应了从代码加载.执行至执行完毕的一个完整过程,这个过程也是进程本身从产生.发展至消亡的过程.线程是比进程更小的单位,一个进程执行过程中可以产生多个线程,每个线程有自身的产生.存在和消亡的过程,也是一个动态的概念.每个进程都有一段专用的内存区域,而线程间可以共享相同的内存区域(包括代码和数据),并利用这些共享单元来实现

(转载)Java多线程入门理解

转载出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢?如果你觉得此文很简单,那推荐你看看Java并发包的的线程池(Java并发编程与技术内幕:线程池深入理解),或者看这个专栏:Java并发编程与技术内幕.你将会对Java里头的高并发场景下的线程有更加深刻的理解. 目录(?)[-] 一扩展javalangThread类 二实现javalan

synchronized与static synchronized 的差别、synchronized在JVM底层的实现原理及Java多线程锁理解

本Blog分为例如以下部分: 第一部分:synchronized与static synchronized 的差别 第二部分:JVM底层又是怎样实现synchronized的 第三部分:Java多线程锁,源码剖析 第一部分:synchronized与static synchronized的差别 1.synchronized与static synchronized 的差别 synchronized是对类的当前实例进行加锁,防止其它线程同一时候訪问该类的该实例的全部synchronized块.注意这里

synchronized与static synchronized 的区别、synchronized在JVM底层的实现原理及Java多线程锁理解

本Blog分为如下部分: 第一部分:synchronized与static synchronized 的区别 第二部分:JVM底层又是如何实现synchronized的 第三部分:Java多线程锁,源代码剖析 第一部分:synchronized与static synchronized的区别 1.synchronized与static synchronized 的区别 synchronized是对类的当前实例进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是"类

Java多线程的理解

一个线程创建之后,总是处于其生命周期的4个状态之一中.线程的状态表明此线程当前正在进行的活动,而线程的状态是可以通过程序来进行控制的,就是说,可以对线程进行操作来改变状态.这些操作包括启动(start).终止(stop).睡眠(sleep).挂起 (suspend).恢复(resume).等待(wait)和通知(notify).每一个操作都对应了一个方法,这些方法是由软件包java.lang提供的. ①创建(new)状态 如果创建了一个线程而没有启动它,那么,此线程就处于创建状态.比如,下述语句

Java多线程的理解和实例

编写具有多线程程序经常会用到的方法:run(), start(), wait(), notify(), notifyAll(), sleep(), yield(), join() 还有一个关键字:synchronized 下面主要来讲讲: 线程的创建方式就不需要细说,就2种方式 Thread和Runnable 1.run()和start() 实例1: public class ThreadTest extends Thread { public void run() { for (int i =

java 多线程的理解 黑马程序员

public class MultiThread { public static void main(String args[]) { System.out.println("我是主线程!"); //下面创建线程实例thread1 ThreadUseExtends thread1=new ThreadUseExtends(); //创建thread2时以实现了Runnable接口的THhreadUseRunnable类实例为参数 Thread thread2=new Thread(ne

java多线程浅谈

经常看到,一个对象的synchronized方法被一个线程调用后,那么其他线程还能调用该线程的其他方法吗? 网上给出各种答案,其中一种是:不能. 但是,我们有没有自己动手去写一个简单的程序来验证一下?从这个问题浅谈一下我对java多线程的理解. 要理解java的多线程,首先的理解jvm. 参见http://blog.csdn.net/kyfg27_niujin/article/details/7942006

Java多线程 -- 单例模式的Double-checked Locking (DCL)问题

Double-checked Locking (DCL)用来在lazy initialisation 的单例模式中避免同步开销的一个方法. 下面是这么做的一个例子. [java] view plaincopy public class MyFactory { private static MyFactory instance; public synchronized static MyFactory getFactory() { if (instance == null) instance =