Java 7之多线程第1篇 – 基础API介绍

来源:互联网 发布:300英雄mac版下载 编辑:程序博客网 时间:2024/05/17 02:17

  一个程序只有一个进程,而一个进程可以包含多个线程,所以进程只能是并发,而线程可以并行。 进程是操作系统中资源分配的基本单位,同一进程的线程间可以共享所属进程的资源,在运行期间,线程才是操作系统的调度和分派的基本单位。同时,操作系统在创建、撤销及切换线程的时候,开销会比进程小。线程在状态转换过程中,可以调用Java API提供的某些方法来改变线程运行的状态。如下图。

下面来介绍一下影响线程运行状态的相关方法。



1、创建及启动Java线程

     

      使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义、实例化和启动一个Java线程。Java中,每个线程都有一个调用栈,即使不在程序中创建任何新的线程,线程也在后台运行着。一个Java应用总是从main()方法开始运行,mian()方法运行在一个线程内,它被称为主线程。

(1)第一种方法:直接继承Thread类,重写父类的run()方法

运行的一种结果如下:

可以看出:一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启动的线程来说,调度程序不能保证其执行次序,持续时间也无法保证。

(2)定义线程类并实现Runnable接口。因为Java是单继承了,如果继承了Thread类就不能继承其他的类了,而接口却是可以实现多个的,同时这种方式还能够为多个线程提供共享的数据

运行的一种结果如下:

2、设置线程的优先级getPriority()和setPriority()


   通过该方法可以设置线程的优先级,一般情况下,优先级越高的线程获得CPU调度的时间片将会越长。java线程的优先级值默认为5,设置范围为1-10.

  因为Java的线程是被映射到系统的原线程上来实现的,所以线程调度最终还是由操作系统说了算的,虽然很多线程都提供线程优先级的概念,但是并不剪得能与java线程的优先级一一对应,如Solaries中有231种优先级,Windows中就只有7中,并不一定能与java线程的优先级设置一一对应,因此,建议还是直接使用java本身自带的三个MIN_PRIORITY(1)、NORM_PRIORITY(5)、MAX_PRIORITY(10)来设置优先级比较好

运行多次后发现,优先级高的一般会最先执行完循环,而最低优先级的一般会最后执行完循环。


3、让出CPU,当前线程进入就绪队列等待调度yield()

t1线程和t2线程输出10个数字后就会进入等待队列把CPU让给了对方,所以当某个线程是10的倍数时,下一个执行的线程一定会是另外一个线程。所以可以从打印的结果看到,t1和t2线程肯定是轮流执行的。

如果注释掉t2线程后,则只有t1线程在执行了。



4、线程睡眠sleep()


指定线程睡眠时间


等待一会儿的时间,可以看到打印出hello world。为什么说一会儿呢,可能还有其他线程也在运行,除非你确保自己的电脑上只有一个线程在运行。 sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行,所以在同步块中一般不使用sleep()方法。


5、判断线程生死isAlive()


对线程的操作,Java是不可能直接做到的,他也是通过JNI本地调用进行操作

其实线程还可以相互影响,下面来介绍一些线程相互作用时用到的方法。



6、线程的暂停与唤醒wait()、notify()和notifyAll()

      

    当前线程进入等待池(wait pool),wait方法通过参数可以指定等待的时长,如果没有指定参数,默认一直等待直到使用notify()方法通知该线程或者notifyAll()方法通知才会继续执行下去。需要注意的是,必须从同步环境内调用wait()、notify()、notifyAll()方法。线程不能调用对象上等待或通知的方法,除非它拥有那个对象的锁。,否则会报以下错误:


wait()、notify()、notifyAll()都是Object的实例方法。与每个对象具有锁一样,每个对象可以有一个线程列表,他们等待来自该信号(通知)。线程通过执行对象上的wait()方法获得这个等待列表。从那时候起,它不再执行任何其他指令,直到调用对象的notify()方法为止。如果多个线程在同一个对象上等待,则将只选择一个线程(不保证以何种顺序)继续执行。如果没有线程等待,则不采取任何特殊操作。


ait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

在使用时注意notify()和notifyAll()方法:

    notify():唤醒一个处于等待状态的线程,不包括当前执行notify()方法的线程。由于是运行注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。 
    Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

最后运行的结果如下:

enter thread1…
thread1 is waiting
enter thread2…
thread2 notify other thread can release wait status..
thread2 is sleeping ten millisecond…
thread2 is going on…
thread2 is being over!
thread1 is going on…
thread1 is being over!




7、线程合并的join()方法

   

调用某线程A的这个方法,将该线程与当前线程B合并,即等待该线程A执行完毕后再继续执行线程B,如下图。

   

   当线程B调用线程A的join方法时,线程B必须等待线程A执行完毕,线程B才能继续往下执行。join方法主要用来将大问题分解成小问题,当小问题计算完成时,大问题才能继续往下执行,这时候我们就可以利用join方法了。


运行结果如下:


8、杀死线程stop()



    一般不使用stop()方法,是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。


ps:转http://blog.csdn.net/mazhimazh/article/details/16906877

0 0