天天看点

JAVA多线程--基本特性

进程与线程

进程:进程是正在运行的程序的实例,它拥有自己独立的地址空间,程序内容和数据。通俗的来讲进程就是一个应用程序。

线程:表示程序的执行流程,是CPU调度执行的基本单位,在JAVA 中线程拥有自己的程序计数器,虚拟机栈。同一进程中的线程公用相同的地址空间,同时共享进程锁拥有的内存和其他资源,一个进程可以拥有多个线程。

线程的状态

线程拥有七种不同的状态:

1.就绪(Runnable):线程准备运行,等待线程调度程序的调度,不一定立马就能开始执行。

2.运行中(Running):当前线程被线程调度程序从可运行池中选中,线程进入运行状态,进程正在执行线程的run()方法。

3.等待中(Waiting):线程处于阻塞的的状态,等待外部处理结束。

4.睡眠中(Sleeping):Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠。当睡眠时间到期后,则返回到就绪状态。

5.I/O阻塞(Blocked on I/O):等待I/O操作完成。

6.同步阻塞(Blocked on Synchronization ):等待获取锁。

7.死亡(Dead):线程完成了执行。

线程的创建

在JAVA中创建线程有两种方法,一是继承Thread类,二是实现Runnable接口,由于JAVA单继承的原因,一般推荐使用第二种。线程的使用中有两个重要的方法:run()和start(),创建线程后调用start()方法启动线程,这时线程进入就绪状态,当被线程调度程序调度时,线程进入运行状态,这时就会执行run()方法,所以线程执行的任务放入run()方法中。

Thread类常用构造器:

public void Thread();

public void Thread(String threadName);

public void Thread(Runnable target);

public void Thread(Runnable target,String threadName);

package com.maiya;

/**
 * 测试线程的创建
 * @author WHF
 */
public class Test {
	public static void main(String[] args) {
		ThreadOne t1=new ThreadOne();
		t1.start();
		
		Thread t2=new Thread(new ThreadTwo());
		t2.start();
	}	
}
/**
 * 继承Thread类
 * @author WHF
 */
class ThreadOne extends Thread{
	public void run() {
		System.out.println("我是ThreadOne");
	}
}
/**
 * 实现Runnable接口
 * @author WHF
 *
 */
class ThreadTwo implements Runnable{
	public void run() {
		System.out.println("我是ThreadTwo");
	}
}
           

线程的休眠

线程的休眠有两个静态方法,在哪个线程的run()方法中调用,哪个线程就会休眠指定的时长(这两个方法较为简单,就不用代码演示了)。

Thread.sleep(long millis);

Thread.sleep(long millis,int nanos);//第一个参数为毫秒,第二个参数为纳秒。

线程的优先级

线程的优先级将线程的重要性传递给线程调度程序。尽管CPU处理现有的线程集的顺序是不明确的,但是调度程序将倾向于让优先级最高的程序先执行。然而这并不是意味着优先级较低的线程得不到执行,优先级较低的程序仅仅是执行的频率较低。在绝大多数的时间里,所有的线程应该以默认的优先级执行。线程的优先级用1--10的整数表示,默认的优先级为5,Thread提供了三个常量优先级:最高级(MAX_PRIORITY),最低级(MIN_PRIORITY),默认(NORM_PRIORITY)。设置线程的优先级使用setPriority(int priority) 方法。
ThreadOne t1=new ThreadOne();
t1.setPriority(Thread.MAX_PRIORITY);
           

线程的让步

如果知道已经完成了在run()方法的循环的一次迭代过程所需的工作,就可以给线程调度机制一个暗示:你的工作已经做得差不多了可以让别的线程使用CPU了。这个暗示将通过调用yeild()方法来作出(使当前运行着的线程让出CPU资源,但是然后给谁并不知道,仅仅是让出,线程状态回到就绪状态)。当调用yield()时,你也是在建议具有相同优先级的其他线程可以运行。和sleep()方法一样,yeild()属于Thread的静态方法,在哪个线程的run()方法中调用,那个线程就会做出让步。

后台线程(守护线程)

所谓后台线程(Daemon),是指在程序云行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可缺少的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程,反过来说只要有程序的非后台线程还在运行,后台线程就不会终止。例如JVM的垃圾回收,内存管理等线程都是后台线程。调用线程对象的setDaemon(true) 方法,则可以将其设置为守护线程,并且必须在线程启动之前调用setDaemon(true)方法,才能将它设置为后台线程。
ThreadOne t1=new ThreadOne();
t1.setDaemon(true);
t1.start();
           

线程的同步与锁

当多个线程同时访问同一个对象时,并且有任一线程对数据有修改操作,这时候读数据的线程读到的数据就是不安全的,因为我们无法控制读到的数据是修改前还是修改后的,这时候就引出了线程的锁的概念。Java中每个对象都有一个内置的锁,当程序运行到非静态的synchronized同步方法时,自动获得该对象的锁。一个对象只有一个锁,所以当一个线程获取该锁其他线程就没法获取,直到第一个线程释放锁,这也就意味着任何其他线程不能进入该对象的synchronized同步方法。在使用synchronized关键字时,只能同步方法和同步代码块,不能同步一个类或变量,当使用同步代码块的时候应该指定在哪个对象同步,也就是说要获取哪个对象的锁,如果方法为静态方法则获取该类的class对象的锁(注意:当调用sleep()和yield()方法时,锁并不会释放)。

同步方法

//同步方法
public synchronized void show(){
	System.out.println("线程锁的测试");
}
           

同步代码块

//同步代码块
public void show(){
	synchronized(this){
		System.out.println("线程锁的测试");
	}
}
           

线程的协作

为了使得任务彼此之间可以协作,以使得多个任务可以一起工作去解决某个问题。当任务协作时,关键问题是这些任务之间的握手。为了实现这种握手,我们使用了与锁类似的基础特性:互斥。在这种情况下,互斥可以确保只有一个任务可以相应某个信号,这样就可以根除任何可能的竞争条件。在互斥之上,我们为任务添加了一条途径,可以将其自身挂起,直至某些外部条件发生了变化,表示是时候让这个任务先前开动为止。 首先我们先来认识java.lang.Object类中的四个方法: void notify()   //唤醒在此对象监视器上等待的单个线程。

void notifyAll()   //唤醒在此对象监视器上等待的所有线程。

void wait()   //导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。

void wait(long timeout)    //导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。 如果调用某个对象的wait()方法,当前线程必须拥有这个对象的锁,因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。调用某个对象的wait()方法,相当于让当前线程交出此对象的锁,然后进入等待状态,等待后续再次获得此对象的锁);notify()方法能够唤醒一个正在等待该对象的锁的线程,当有多个线程都在等待该对象的锁的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。notiryAll()则会唤醒所有正在等待该对象锁的线程。一个线程被唤醒不代表立即获取了对象的锁,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。

package com.maiya;

public class Test {
    public static Object object = new Object();
    public static void main(String[] args) {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
         
        thread1.start();
         
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         
        thread2.start();
    }
     
    static class Thread1 extends Thread{
        @Override
        public void run() {
            synchronized (object) {     
            	try {
		<span style="white-space:pre">	</span>object.wait();//释放object对象的锁,线程进入等待状态
			System.out.println("线程"+Thread.currentThread().getName()+"获取到了锁");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}  
                
            }
        }
    }
     
    static class Thread2 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                object.notify();//唤醒等待object对象锁的线程
                System.out.println("线程"+Thread.currentThread().getName()+"调用了object.notify()");
                System.out.println("线程"+Thread.currentThread().getName()+"即将释放锁");
            }
        }
    }
}
           
//输出结果
线程Thread-1调用了object.notify()
线程Thread-1即将释放锁
线程Thread-0获取到了锁
           
参见:http://lavasoft.blog.51cto.com/62575/27069/