码迷,mamicode.com
首页 > 编程语言 > 详细

java 多线程初探

时间:2015-04-09 10:36:24      阅读:181      评论:0      收藏:0      [点我收藏+]

标签:java 多线程   java   

一直都很想写关于多线程的东西,以来可以巩固巩固自己的知识,而来可以看看自己的掌握的水平,因为一直都觉得这方面挺有意思的好了。废话不多说,入正题。
java多线程,我们首先想多的是什么。进程,Thread,Runnable,start,run...
那我们就先从他们入手了。为什么会想到进程呢。以为一直都是多线程多进程的说。那他们有什么区别。
进程:进程是程序的运行和操作系统分配资源的最基本的独立单位。每个进程在没有特殊的处理下,是各自独立的。
线程呢:线程不能独立存在,线程必须依附于进程,线程共享进程的资源,从某种意义上来说,使用线程对系统开销更小,效率应该高点。
在java中,主要两种方式能够实现多线程的编程。一种是继承Thread类,一种是实现Runnable接口。要说两者的区别:我觉得无非是
1.Runnable更适合线程间的资源共享,也更推荐使用Runnable。
2.Runnable是接口所以可以避免Thread单继承的缺陷。
好了,这只是嘴上说说而已,还是来看看具体代码吧。
我们来分别实现一个经典的窗口买票程序:
    继承Thread:
<span style="font-size:14px;">class SalerThread extends Thread{
    private int sum = 5;


    public void sale(){
        System.out.println(getName() + "号窗口正在售票,还剩" + (--sum) + "张票.");
    }


    @Override
    public void run(){
        while (sum > 0) {
            sale();
        }
    }
}
public class TestThread {
    public static void main(String[] args) {
        SalerThread thread = new SalerThread();
        SalerThread thread1 = new SalerThread();
        SalerThread thread2 = new SalerThread();
        thread.setName("1");
        thread1.setName("2");
        thread2.setName("3");
        thread.start();
        thread1.start();
        thread2.start();
	}
}


	我们可以看到结果,很明显不符合我们的实际情况,但是但我们换成实现Runnable呢!
class SalerRunnale implements Runnable{
    private int sum = 5;


    public void sale(){
            System.out.println(Thread.currentThread().getName() + "号窗口正在售票,还剩" + (--sum) + "张票.");
    }


    @Override
    public void run() {
        while (sum > 0) {
            sale();
        }
    }
}
public class TestThread {
    public static void main(String[] args) {
        SalerRunnale runnale = new SalerRunnale();
        Thread thread = new Thread(runnale, "1");
        Thread thread1 = new Thread(runnale, "1");
        Thread thread2 = new Thread(runnale, "1");
        thread.start();
        thread1.start();
        thread2.start();
    }
}</span>


两个线程主体基本一样,就是在调用Thread中的方法时,我们需要多写一步Thread.currentThread.

也许你想问了,为什么我们是调用start()呢,我们当然也可以调用run()啦,我保证,编译器不会报错,而且有结果,但是,这个结果是按照我们调用所谓的线程的顺序输出的。

技术分享

也许你会想,哼,肯定只是偶然,但当我们多次调用之后,结果依然不变。好了,其实,在直接调用run()方法时,jvm只是把它当做一个普通方法进行调用,并不会给他重新分配一个线程。所以一定要记住,在写新线程的时候,调用的是它的start()方法,而不是我们重写的run()方法。

好了,现在聊一聊多线程的一些状态:
线程可以有6种状态:
*New(新创建) 
*Runnable(可运行)
*Blocked(被阻塞)
*Waiting(阻塞)
*Timed waiting(计时等待)
*Terminated(被终止)
技术分享                                                                                  摘自:http://my.oschina.net/mingdongcheng/blog/139263
线程之间是有自己的优先级的,相信这个词你应该很熟悉。默认情况下,子线程是继承父线程的优先级,当然,你也可以通过调用setPriority方法来设置优先级。线程的优先级应该在MIN_PRIORITY(Thread中定义为1)到MAX_PRIORITY(Thread中定义为10)。
有了jvm默认是先调用优先级高的线程,默认的是NORM_PRIORITY(Thread中定义5),当我们在线程中调用yield()时,会使当前线程处于一种让步的状态,即先让不低于自己的程序先运行,假如没有比自己高的呢,依旧自己运行。当然这不是绝对的,他依靠操作系统的支持。

有一种线程叫做守护线程。顾名思义,他是需要被守护者的,假如只有他自己则没有意义。所以,他不会独立存在。调用他们非常简单。只需要在start()之前调用setDeamon(true)即可。
但是,他依旧有缺陷,守护进程不应该去访问固有资源,如文件、数据库,因为他会在任何时候甚至在一个操作的中间发生中断。


我们在一个线程运行到一半想终止他怎么办。java一开始提供了stop方法,但是因为他很不安全,所以被弃用了。不仅如此,java还提供线程挂起的方法suspend和唤醒的方法resume。不过suspend很容易导致死锁的情况,所以也已经被抛弃了。
但是假如我们需要暂停一个线程怎么办。
我们可以设置一个boolean值。
如private boolean running;
需要暂停的时候
public synchronized void stop(){
running = false;
}
而我们在run方法中假如running的条件判断
while (running){
//your code...
}
如此,我们便实现了自己的暂停方法。当然才学到这的你可能会问为什么要加synchronized呢,他是什么意思呢。这个问题我们稍后会介绍,当然,你也可以看看我之前写的那一篇博客---超链接---。

好了,到这,我们还需要说一下线程的中断。线程在执行完run方法最后一条语句后,或者有未捕获的异常时,线程会终止。当然,我们可以调用interrupt方法来终止线程。每个线程都有表示中断状态的boolean值。每个线程都应该不时检查这个标志位,当然,假如在调用它之前,线程已经调用了wait,sleep或者join,他的interrupt status将会被清除,并且会抛出一个InterruptedException.异常。

 Thread还有一个join方法,在一个线程A中调用另一个线程B,线程B调用join,那么线程A将等待线程B执行完毕。或者调用join的重载方法,设置时间表示之多等待多久。

<span style="font-size:14px;"><span style="font-size:12px;">class RunnableA implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5; i++){
            System.out.println("threadA :" + i);
        }
    }
}

public class TestForJoin {
    public static void main(String[] args) throws InterruptedException {
        RunnableA runnableA = new RunnableA();
        Thread threadA = new Thread(runnableA);
        threadA.start();
//        threadA.join();
        for (int i = 0; i < 5; i++){
            System.out.println(Thread.currentThread().getName() + " :" + i);
        }
    }
}</span></span>
当我们注释threadA.join();时,结果如图:

技术分享

当我们取消注释:

技术分享

假如是独立的两个线程,一个线程调用join,对另外一个线程是不起作用的。

<span style="font-size:14px;"><span style="font-size:12px;">public class TestForJoin {
    public static void main(String[] args) throws InterruptedException {
        RunnableA runnableA = new RunnableA();
        Thread threadA = new Thread(runnableA, "ThreadA");
        Thread ThreadB = new Thread(runnableA, "ThreadB");
        threadA.start();
        threadB.start();
        threadA.join();
        for (int i = 0; i < 5; i++){
            System.out.println(Thread.currentThread().getName() + " :" + i);
        }
    }
}</span></span>
因为在一个线程A中调用另一个线程B,B线程调用join,这只会阻塞A线程,假如同时存在线程C,C依旧可以和B抢占CPU.
	我们说过线程之间的资源出自身的一些变量方法,其余都共享,所以,多线程必定会产生多个线程同时修改一块内存块的问题。我们该如何避免呢。
	最简单也是最有效的方法当然是不使用多线程啦。但是当我们必须使用多线程的时候我们该怎么办。
	我们先来做一个小程序,多线程按顺序打印1-9,要求先打印1-3,在打印4-6,最后全部输出。
<span style="font-size:14px;"><span style="font-size:12px;">public class ThreadPrint {
    static Lock lock = new ReentrantLock();
    static Condition reachThree = lock.newCondition();
    static Condition reachSix = lock.newCondition();

    public static void main(String[] args) {
        final int[] integer = {0};

        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    for (int i = 1; i <= 3; i++) {
                        System.out.println(i);
                    }
                    integer[0] = 3;
                    reachThree.signal();
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    while (integer[0] != 3) {
                        reachThree.await();
                    }
                    for (int i = 4; i <= 6; i++) {
                        System.out.println(i);
                    }
                    integer[0] = 6;
                    reachSix.signal();
                } catch (InterruptedException e) {

                } finally {
                    lock.unlock();
                }
            }
        });

        Thread threadC = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    while (integer[0] != 6) {
                        reachSix.await();
                    }
                    for (int i = 6; i <= 9; i++) {
                        System.out.println(i);
                    }
                    integer[0] = 9;
                } catch (InterruptedException e) {

                } finally {
                    lock.unlock();
                }
            }
        });
        threadA.start();
        threadB.start();
        threadC.start();
    }

}</span></span>

Lock是concurrent中给我们提供的一种锁机制,当一个线程进入一个类时,便给它加上锁,当还有其他的线程在之前线程还未退出,请求范文该类时,就会被阻塞,直至第一个进入的线程退出并解除锁。condition是一个条件,一个锁可以有一个或多个条件。当线程因为一个条件被阻塞时,会自动释放它的锁,一边其他线程进入并唤醒它。我们调用的锁ReentrantLock是一种可重入的锁,但一个线程进入一个类,调用一个含锁的方法时,它的计数器会加1,当释放一个锁时便减一,每次有其他线程进入该类会检查计数器是否为0.我们还可以在调用构造函数时将它设置成公平锁new ReentrantLock(true),即等待时间越长越有可能获得该锁,但是它的效率相比不公平锁会低很多,所以不是必须使用它,我们总是使用缺省的不公平锁。
	当然还有一种ReentrantReadWriteLock,当线程频繁对数据进行读操作时,他是一个很好的选择,它有读锁和写锁,它允许多个线程进行读取,指定数量的线程进行写操作。所以效率高。每次进行读操作时利用ReentrantReadWriteLock.ReadLock,写操作便用ReentrantReadWriteLock。
	当然java还提供一种更为简单的机制synchronized,可以设置同步块和同步方法,具体可以参考我的另一篇博文:初探java 对象中wait(),notify(),notifyAll() 和线程中的synchronized。需要多说的是,synchronized调用的实际上是一个内部锁,它只含有一个条件。
	你会想那什么时候用synchronized什么时候用Lock呢。
	1.其实最好是两个都不用,因为在我们的concurrent包中,有同步队列,他会自动帮我们处理数据的同步问题。
	2.如果synchronized能够符合我们要求时,尽量使用它。
	3.当我们需要用到Lock/Condition的特殊功能时,才考虑使用它。

	


java 多线程初探

标签:java 多线程   java   

原文地址:http://blog.csdn.net/u010233260/article/details/44954659

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!