您的位置:首页 > 编程语言 > Java开发

java多线程理解

2015-08-25 21:04 399 查看
   实现线程的方式有两种:

1、继承java.lang.Thread,并重写它的run()方法,将线程的执行主体放入其中。

2、实现java.lang.Runnable接口,实现它的run()方法,并将线程的执行主体放入其中。

==>多线程的执行逻辑:

当主线程被挂起时, 其它就绪的线程会根据选择最高优先级的来执行;

当主线程的挂起时间 > 子线程的执行时间时,子线程执行完后回到主线程,等待主线程醒来.

当主线程的挂起时间 < 子线程的执行时间时,主线程挂起时间到的,自动醒来,回到主线程,此时可以判断子线程是否存在,若有,可stop之.

(2)

两种方式的区别:

继承Thread类的方式实现起来较为简单,但是继承它的类就不能再继承别的类了,因此也就不能继承别的类的有用的方法了。而使用是想Runnable 接口的方式就不存在这个问题了,而且这种实现方式将线程主体和线程对象本身分离开来,逻辑上也较为清晰,所以推荐大家更多地采用这种方式。

run()方法中包含的是线程的主体,也就是这个线程被启动后将要运行的代码,它跟线程的启动没有任何关系。

    在Java 1.4及以下的版本中,每个线程都具有新建、可运行、阻塞、死亡四种状态,但是在Java 5.0及以上版本中,线程的状态被扩充为新建、可运行、阻塞、等待、定时等待、死亡六种。线程的状态完全包含了一个线程从新建到运行,最后到结束的整个生 命周期。线程状态的具体信息如下:

   1. NEW(新建状态、初始化状态):线程对象已经被创建,但是还没有被启动时的状态。这段时间就是在我们调用new命令之后,调用start()方法之前。
   2. RUNNABLE(可运行状态、就绪状态):在我们调用了线程的start()方法之后线程所处的状态。处于RUNNABLE状态的线程在JAVA虚拟机(JVM)上是运行着的,但是它可能还正在等待操作系统分配给它相应的运行资源以得以运行。

   3. BLOCKED(阻塞状态、被中断运行):线程正在等待其它的线程释放同步锁,以进入一个同步块或者同步方法继续运行;或者它已经进入了某个同步块或同步 方法,在运行的过程中它调用了某个对象继承自java.lang.Object的wait()方法,正在等待重新返回这个同步块或同步方法。

   4. WAITING(等待状态):当前线程调用了java.lang.Object.wait()、 java.lang.Thread.join()或者java.util.concurrent.locks.LockSupport.park()三个 中的任意一个方法,正在等待另外一个线程执行某个操作。比如一个线程调用了某个对象的wait()方法,正在等待其它线程调用这个对象的notify() 或者notifyAll()(这两个方法同样是继承自Object类)方法来唤醒它;或者一个线程调用了另一个线程的join()(这个方法属于
Thread类)方法,正在等待这个方法运行结束。

   5. TIMED_WAITING(定时等待状态):当前线程调用了 java.lang.Object.wait(long timeout)、java.lang.Thread.join(long millis)、java.util.concurrent.locks.LockSupport.packNanos(long nanos)、java.util.concurrent.locks.LockSupport.packUntil(long deadline)四个方法中的任意一个,进入等待状态,但是与WAITING状态不同的是,它有一个最大等待时间,即使等待的条件仍然没有满足,只要到
了这个时间它就会自动醒来。

   6. TERMINATED(死亡状态、终止状态):线程完成执行后的状态。线程执行完run()方法中的全部代码,从该方法中退出,进入TERMINATED状态。还有一种情况是run()在运行过程中抛出了一个异常,而这个异常没有被程序捕获,导致这个线程异常终止进入TERMINATED状态。

    线程的状态转换图
线程在一定条件下,状态会发生变化。线程变化的状态转换图如下:



 
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()方法,该线程结束生命周期。
 
   线程的调度
1、调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会。
 
Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORITY
          线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY
          线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY
          分配给线程的默认优先级,取值为5。
 
Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。
 
每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。
JVM提供了10个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用Thread类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式。
 
2、线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。
 
3、线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。
 
4、线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
 
5、线程加入:join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
 
6、线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。
 
注意:Thread中suspend()和resume()两个方法在JDK1.5中已经废除,不再介绍。因为有死锁倾向。
 
7、常见线程名词解释
主线程:JVM调用程序mian()所产生的线程。
当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。
前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。

[align=center]Java多线程编程总结 http://lavasoft.blog.51cto.com/62575/27069/[/align]

Java线程:并发协作-生产者消费者模型
 
对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的。就像学习每一门编程语言一样,Hello World!都是最经典的例子。
 
实际上,准确说应该是“生产者-消费者-仓储”模型,离开了仓储,生产者消费者模型就显得没有说服力了。
对于此模型,应该明确一下几点:
1、生产者仅仅在仓储未满时候生产,仓满则停止生产。
2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。
3、当消费者发现仓储没产品可消费时候会通知生产者生产。
4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。
 
此模型将要结合java.lang.Object的wait与notify、notifyAll方法来实现以上的需求。这是非常重要的。
 

/**

* Java线程:并发协作-生产者消费者模型

*

* @author leizhimin 2009-11-4 14:54:36

*/
public class Test {

        public staticvoid main(String[] args) {

                Godown godown = new Godown(30);

                Consumer c1 = new Consumer(50, godown);

                Consumer c2 = new Consumer(20, godown);

                Consumer c3 = new Consumer(30, godown);

                Producer p1 = new Producer(10, godown);

                Producer p2 = new Producer(10, godown);

                Producer p3 = new Producer(10, godown);

                Producer p4 = new Producer(10, godown);

                Producer p5 = new Producer(10, godown);

                Producer p6 = new Producer(10, godown);

                Producer p7 = new Producer(80, godown);

                c1.start();

                c2.start();

                c3.start();

                p1.start();

                p2.start();

                p3.start();

                p4.start();

                p5.start();

                p6.start();

                p7.start();

        }

}

/**

* 仓库

*/
class Godown {

        public staticfinal
int max_size = 100;//最大库存量

        public int curnum;    //当前库存量

        Godown() {

        }

        Godown(int curnum) {

                this.curnum = curnum;

        }

        /**

         * 生产指定数量的产品

         *

         * @param neednum

         */

        public synchronizedvoid produce(int neednum) {

                //测试是否需要生产

                while (neednum + curnum > max_size) {

                        System.out.println("要生产的产品数量" + neednum +"超过剩余库存量" + (max_size - curnum) +
",暂时不能执行生产任务!");

                        try {

                                //当前的生产线程等待

                                wait();

                        } catch (InterruptedException e) {

                                e.printStackTrace();

                        }

                }

                //满足生产条件,则进行生产,这里简单的更改当前库存量

                curnum += neednum;

                System.out.println("已经生产了" + neednum +"个产品,现仓储量为" + curnum);

                //唤醒在此对象监视器上等待的所有线程

                notifyAll();

        }

        /**

         * 消费指定数量的产品

         *

         * @param neednum

         */

        public synchronizedvoid consume(int neednum) {

                //测试是否可消费

                while (curnum < neednum) {

                        try {

                                //当前的生产线程等待

                                wait();

                        } catch (InterruptedException e) {

                                e.printStackTrace();

                        }

                }

                //满足消费条件,则进行消费,这里简单的更改当前库存量

                curnum -= neednum;

                System.out.println("已经消费了" + neednum +"个产品,现仓储量为" + curnum);

                //唤醒在此对象监视器上等待的所有线程

                notifyAll();

        }

}

/**

* 生产者

*/
class Producer extends Thread {

        private int neednum;                //生产产品的数量

        private Godown godown;            //仓库

        Producer(int neednum, Godown godown) {

                this.neednum = neednum;

                this.godown = godown;

        }

        public void run() {

                //生产指定数量的产品

                godown.produce(neednum);

        }

}

/**

* 消费者

*/
class Consumer extends Thread {

        private int neednum;                //生产产品的数量

        private Godown godown;            //仓库

        Consumer(int neednum, Godown godown) {

                this.neednum = neednum;

                this.godown = godown;

        }

        public void run() {

                //消费指定数量的产品

                godown.consume(neednum);

        }

}

 

已经生产了10个产品,现仓储量为40

已经生产了10个产品,现仓储量为50

已经消费了50个产品,现仓储量为0

已经生产了80个产品,现仓储量为80

已经消费了30个产品,现仓储量为50

已经生产了10个产品,现仓储量为60

已经消费了20个产品,现仓储量为40

已经生产了10个产品,现仓储量为50

已经生产了10个产品,现仓储量为60

已经生产了10个产品,现仓储量为70

Process finished with exit code 0

 
说明:
对于本例,要说明的是当发现不能满足生产或者消费条件的时候,调用对象的wait方法,wait方法的作用是释放当前线程的所获得的锁,并调用对象的notifyAll() 方法,通知(唤醒)该对象上其他等待线程,使得其继续执行。这样,整个生产者、消费者线程得以正确的协作执行。
notifyAll() 方法,起到的是一个通知作用,不释放锁,也不获取锁。只是告诉该对象上等待的线程“可以竞争执行了,都醒来去执行吧”。
 
本例仅仅是生产者消费者模型中最简单的一种表示,本例中,如果消费者消费的仓储量达不到满足,而又没有生产者,则程序会一直处于等待状态,这当然是不对的。实际上可以将此例进行修改,修改为,根据消费驱动生产,同时生产兼顾仓库,如果仓不满就生产,并对每次最大消费量做个限制,这样就不存在此问题了,当然这样的例子更复杂,更难以说明这样一个简单模型。

进程分为基本的三个状态:运行、就绪、阻塞/等待。

A. 高优先级的抢占CPU,使得原来处于运行状态的进程转变为就绪状态。

B. 阻塞的进程等待某件事情的发生,一旦发生则它的运行条件已经满足,从阻塞进入就绪状态。

C. 时间片轮转使得每个进程都有一小片时间来获得CPU运行,当时间片到时从运行状态变为就绪状态。

D. 自旋锁(spinlock)是一种保护临界区最常见的技术。在同一时刻只能有一个进程获得自旋锁,其他企图获得自旋锁的任何进程将一直进行尝试(即自旋,不断地测试变量),除此以外不能做任何事情。因此没有获得自旋锁的进程在获取锁之前处于忙等(阻塞状态)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: