java多线程学习

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

一、扩展java.lang.Thread类

package com.multithread.learning;
/**
 *@functon 多线程学习
 *@author 刁兆建
 *@time 2016.10.17
 */
class Thread1 extends Thread{
    private String name;
    public Thread1(String name) {
       this.name=name;
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "运行  :  " + i);
            try {
                sleep((int) Math.random() * 10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
       
    }
}
public class Main {

    public static void main(String[] args) {
        Thread1 mTh1=new Thread1("A");
        Thread1 mTh2=new Thread1("B");
        mTh1.start();
        mTh2.start();

    }

}

输出:

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

再运行一下:

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

注意:

Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。
实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。

但是start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。

		Thread1 mTh1=new Thread1("A");
		Thread1 mTh2=mTh1;
		mTh1.start();
		mTh2.start();

输出:

Exception in thread "main" java.lang.IllegalThreadStateException
    at java.lang.Thread.start(Unknown Source)
    at com.multithread.learning.Main.main(Main.java:31)
A运行  :  0
A运行  :  1
A运行  :  2
A运行  :  3
A运行  :  4

二、实现java.lang.Runnable接口

/**
 *@functon 多线程学习
 *@author 刁兆建
 *@time 2016.10.17
 */
package com.multithread.runnable;
class Thread2 implements Runnable{
    private String name;

    public Thread2(String name) {
        this.name=name;
    }

    @Override
    public void run() {
          for (int i = 0; i < 5; i++) {
                System.out.println(name + "运行  :  " + i);
                try {
                    Thread.sleep((int) Math.random() * 10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        
    }
    
}
public class Main {

    public static void main(String[] args) {
        new Thread(new Thread2("C")).start();
        new Thread(new Thread2("D")).start();
    }

}

输出:

C运行  :  0
D运行  :  0
D运行  :  1
C运行  :  1
D运行  :  2
C运行  :  2
D运行  :  3
C运行  :  3
D运行  :  4
C运行  :  4

说明:
Thread2类通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。

三、Thread和Runnable的区别

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

package com.multithread.learning;
/**
 *@functon 多线程学习,继承Thread,资源不能共享
 *@author 刁兆建
 *@time 2016.10.17
 */
class Thread1 extends Thread{
    private int count=5;
    private String name;
    public Thread1(String name) {
       this.name=name;
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "运行  count= " + count--);
            try {
                sleep((int) Math.random() * 10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
       
    }
}

public class Main {

    public static void main(String[] args) {
        Thread1 mTh1=new Thread1("A");
        Thread1 mTh2=new Thread1("B");
        mTh1.start();
        mTh2.start();

    }

}

输出:

B运行  count= 5
A运行  count= 5
B运行  count= 4
B运行  count= 3
B运行  count= 2
B运行  count= 1
A运行  count= 4
A运行  count= 3
A运行  count= 2
A运行  count= 1

从上面可以看出,不同的线程之间count是不同的,这对于卖票系统来说就会有很大的问题,当然,这里可以用同步来作。这里我们用Runnable来做下看看:

/**
 *@functon 多线程学习 继承runnable,资源能共享
 *@author 刁兆建
 *@time 2016.10.17
 */
package com.multithread.runnable;
class Thread2 implements Runnable{
    private int count=15;
    @Override
    public void run() {
          for (int i = 0; i < 5; i++) {
              System.out.println(Thread.currentThread().getName() + "运行  count= " + count--);
                try {
                    Thread.sleep((int) Math.random() * 10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        
    }
    
}
public class Main {

    public static void main(String[] args) {
        
        Thread2 my = new Thread2();
            new Thread(my, "C").start();//同一个mt,但是在Thread中就不可以,如果用同一个实例化对象mt,就会出现异常   
            new Thread(my, "D").start();
            new Thread(my, "E").start();
    }

}

输出:

C运行  count= 15
D运行  count= 14
E运行  count= 13
D运行  count= 12
D运行  count= 10
D运行  count= 9
D运行  count= 8
C运行  count= 11
E运行  count= 12
C运行  count= 7
E运行  count= 6
C运行  count= 5
E运行  count= 4
C运行  count= 3
E运行  count= 2

这里要注意每个线程都是用同一个实例化对象,如果不是同一个,效果就和上面的一样了!

总结:

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

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

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

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

提醒一下:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。

三、常用函数说明

①sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)

②join():指等待t线程终止。

/**
 *@functon 多线程学习,join
 *@author 林炳文
 *@time 2015.3.9
 */
package com.multithread.join;
class Thread1 extends Thread{
    private String name;
    public Thread1(String name) {
        super(name);
       this.name=name;
    }
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 线程运行开始!");
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程"+name + "运行 : " + i);
            try {
                sleep((int) Math.random() * 10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + " 线程运行结束!");
    }
}

public class Main {

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
        Thread1 mTh1=new Thread1("A");
        Thread1 mTh2=new Thread1("B");
        mTh1.start();
        mTh2.start();
        System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");

    }

}

输出结果:
main主线程运行开始!
main主线程运行结束!
B 线程运行开始!
子线程B运行 : 0
A 线程运行开始!
子线程A运行 : 0
子线程B运行 : 1
子线程A运行 : 1
子线程A运行 : 2
子线程A运行 : 3
子线程A运行 : 4
A 线程运行结束!
子线程B运行 : 2
子线程B运行 : 3
子线程B运行 : 4
B 线程运行结束!

发现主线程比子线程早结束

加join

public class Main {

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
        Thread1 mTh1=new Thread1("A");
        Thread1 mTh2=new Thread1("B");
        mTh1.start();
        mTh2.start();
        try {
            mTh1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            mTh2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");

    }

}

运行结果:
main主线程运行开始!
A 线程运行开始!
子线程A运行 : 0
B 线程运行开始!
子线程B运行 : 0
子线程A运行 : 1
子线程B运行 : 1
子线程A运行 : 2
子线程B运行 : 2
子线程A运行 : 3
子线程B运行 : 3
子线程A运行 : 4
子线程B运行 : 4
A 线程运行结束!
主线程一定会等子线程都结束了才结束

四、线程同步:

synchronized关键字  synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。

1.  把synchronized当作函数修饰符时,示例代码如下:

Public synchronized void methodAAA()

{

//….

}

public void methodAAA()

{

synchronized (this)      //  (1)

{

       //…..

}

}

原文地址:https://www.cnblogs.com/diaozhaojian/p/5971324.html