`
Technoboy
  • 浏览: 153796 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

Java Concurrent Programming (1)

    博客分类:
  • J2SE
阅读更多
1. 线程概述
  线程(thread)的英文原意是"细丝",Java语言把"正在执行程序的主体"称为线程。如果"在某一时间点进行的处理"只有一个,或者是说"正在执行的程序主体"只有一个,则称之为单线程程序(single threaded program)。如下例:
public static void main(String[] args) {
		for(int i = 0; i < 100; i ++){
			System.out.println("Java Concurrent Programming");
		}
	}

那么,由一个以上的线程所构成的程序就称之为多线程程序(multithreaded program)。如下例:
public class MyThread extends Thread{
	
	public void run(){
		for(int i = 0; i < 100; i ++){
			System.out.println("Java Concurrent Programming");
		}
	}
	
	public static void main(String[] args) {
		new MyThread().start();
	}
}

start方法是Thread类的方法,如果调用start方法,就会启动新的线程。新启动的线程的操作写在run方法里,启动了新的线程之后,这个线程就会调用run方法。当run方法执行结束,此线程也就结束了。如果我们直接调用new MyThread().run()也是可以的,只是这样不会启动新的线程。

1.1 线程的启动
  继承Thread类或实现Runnable接口。
上面的例子中,就是通过子类继承Thread类,然后通过调用start方法启动一个新的线程。我们也可以通过实现Runnable接口来创建新的线程并启动:
public class MyRunnable implements Runnable{

	public void run() {
		for(int i = 0; i < 100; i ++){
			System.out.println("Java Concurrent Programming");
		}
	}
	
	public static void main(String[] args) {
		new Thread(new MyRunnable()).start();
	}
}

Thread类和Runnable接口:
Runnable接口声明如下:
public interface Runnable {
	   
	    public abstract void run();
	    
	}

Thread类实现了Runnable接口
public class Thread implements Runnable {

其run方法:
public void run() {
	if (target != null) {
	    target.run();
	}
    }

这里的target是通过Thread类得public Thread(Runnable target, String name)这个构造方法传入进来的。通常Thread类得run方法都是被子类覆盖的。

1.2 线程的暂时停止
  利用Thread类的sleep方法可以暂时停止线程的执行操作。Thread类的sleep方法有两个版本:
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) 
    throws InterruptedException

第一个版本的sleep方法所设置的暂时停止时间并不是很精密的。如果需要实时的精密控制,可以使用第二个版本的sleep方法。

1.3 线程的中断
  每个线程都有一个与之相关联的Boolean属性,用于表示线程的中断状态(interrupted status)。中断状态初始时为false;当另一个线程通过调用 Thread.interrupt()中断一个线程时,会出现以下两种情况之一。如果那个线程在执行一个低级可中断阻塞方法,例如Thread.sleep()、Thread.join()或 Object.wait(),那么它将取消阻塞并抛出 InterruptedException。否则, interrupt()只是设置线程的中断状态。在被中断线程中运行的代码以后可以轮询中断状态,看看它是否被请求停止正在做的事情。中断状态可以通过 Thread.isInterrupted()来读取,并且可以通过一个名为Thread.interrupted()的操作读取和清除。
  中断是一种协作机制。当一个线程中断另一个线程时,被中断的线程不一定要立即停止正在做的事情。相反,中断是礼貌地请求另一个线程在它愿意并且方便的时候停止它正在做的事情。有些方法,例如 Thread.sleep(),很认真地对待这样的请求,但每个方法不是一定要对中断作出响应。对于中断请求,不阻塞但是仍然要花较长时间执行的方法可以轮询中断状态,并在被中断的时候提前返回。您可以随意忽略中断请求,但是这样做的话会影响响应。
  中断的协作特性所带来的一个好处是,它为安全地构造可取消活动提供更大的灵活性。我们很少希望一个活动立即停止;如果活动在正在进行更新的时候被取消,那么程序数据结构可能处于不一致状态。中断允许一个可取消活动来清理正在进行的工作,恢复不变量,通知其他活动它要被取消,然后才终止。

1.4 线程的共享互斥
  在多线程程序里,多个线程是可以自由操作的,那么就可能同时操作到同一实例。如果要限制某个线程正在执行的这个部分不被其他线程执行时,就称为共享互斥。Java语言在处理线程的共享互斥时,使用synchronized关键字。

1.5 synchronized关键字
  synchronized关键字不属于方法签名的一部分,所以当子类覆盖父类时,synchronized修饰符不会被继承,因此接口中的方法不能被声明为synchronized,同样构造函数也不能被声明为synchronized。在java语言中,锁是递归的,并且是基于"每线程"而不是"每调用"。锁的申请和释放操作是在使用synchronized关键字的时候根据内部的获得-释放协议来使用的。所有的锁都是块结构。当进入synchronized方法或块的时候得到锁,退出的时候释放锁,即使因为异常也会释放锁。

1.6 synchronized修饰类成员函数
  当把synchronized关键字作为类成员函数修饰符时,这时候锁定的是被调用同步方法的对象,如下例:
public synchronized int increment(){
		return ++value;
	}

它等同于
public int increment(){
		synchronized(this){
			return ++ value;
		}
	}

看这个例子Bank类:
public class Bank {
	
	private int money;
	
	public synchronized void deposit(int m){
		money += m;
	}
	
	public synchronized int balance(){
		return money;
	}
	
	public String getAccountName(){
		return "Technoboy";
	}
}

BankTest类:
public class BankTest {
	
	public static void main(String[] args) {
		final Bank bank = new Bank();
		Runnable r1 = new Runnable(){
			public void run(){
				bank.balance();
			}
		};
		Runnable r2 =new Runnable(){
			public void run(){
				bank.deposit(10);
			}
		};
		
		new Thread(r1).start();
		new Thread(r2).start();
	}
}

当两个线程同时调用相同Bank实例方法时,就会对synchronized关键字修饰的方法进行锁定。r1此时拿到bank实例对象锁后,r2想拿到bank实例对象锁,需要等待r1释放bank对象锁,也就是执行完毕balance方法。然而,有无锁定对于非synchronized关键字修饰的方法,完全没有影响,也就是说,多个线程可以同时进入非synchronized方法。

当bank实例对象锁被释放后,刚才因为没有获取锁的多个线程开始抢夺bank实例对象的锁,并且一定会有一个线程获取锁。若等待的线程不只一个,没抢到锁的线程就继续等待。


1.7 synchronized修饰类静态函数
  当把synchronized关键字作为类静态函数的修饰符时,这时候锁定的是被调用同步方法的类对象
public static synchronized int increment(){
		return ++value;
	}

  等同于
public int increment(){
		synchronized(DummyThread.class){
			return ++ value;
		}
	}

  换句话说,synchronized类方法是使用该类的类对象的锁定去做的线程共享互斥,DummyThread.class是对应DummyThread类的java.lang.Class类的实例。我们知道,每个类只有一个Class类的实例。
  注意:当一个static method被调用的时候,程序会在调用此method前取得class的lock。这种机制与非static method相同,他们只是不同的lock而已。而此lock只给static method运用。除了这两种lock在功能上的关联性,他们在操作上毫无关联。这是两种不同的lock。class lock可以用于object lock之外被独立得取得与释放。如果一个非static synchronized method 调用了static synchronized method,它会取得两者的lock。

1.8 同步块
  当有一个明确的对象作为同步锁的时候, 就可以使用同步块。另外, 如果只是想同步一段代码,那么可以创建一个特殊的对象来充当锁
private static Object lock = new Object();
	
	public void increment(){
		synchronized(lock){
			//
		}
	}

线程的共享互斥架构称为监视(monitor),而获取锁定有时也称为持有监视。

1.9 线程的协调
  Java里有wait,notify,notifyAll三个方法依据"空间是否有空闲"为条件进行线程的不同处理。wait是让线程等候,notify和notifyAll则是启动等候中的线程。使用wait方法时,线程变进入wait set(类似线程的休息室,所有实例都有一个wait set),此时线程变暂时停止操作。如欲执行wait方法,线程必须获取锁定。但是当线程进入wait set时,线程就已经释放了该实例的锁定。如果一个线程进入wait set,只有发生以下一种情况,否则线程将永远的留在这个wait set里面:
  1)有其他线程以notify方法唤醒该线程
  2)有其他线程以notifyAll方法唤醒该线程
  3)有其他线程以interrupt方法唤醒该线程
  4)wait方法已经到期
 
当调用wait方法时,会执行如下操作:
  1)当前线程被放入wait set中 
  2)如果当前线程已经被中断,那么该方法立刻退出,然后抛出一个InterruptedException异常。否则线程会被阻塞。 
  3)释放当前对象的同步锁,当线程重新执行时,将重新获取目标对象锁。 

当调用notify方法时,会执行以下操作:
  1)从wait set里抓出一个线程。如果wait set中有多个线程,抓出哪个线程是随机的。
  2)抓出的线程需要重新获得目标对象的同步锁,notify会释放目标对象的同步锁。
  3)抓出的线程从执行wait的那点恢复执行。
同样,线程也是必须拥有目标对象的锁定后,才能执行notify方法。
如果没有持有目标对象锁的线程去调用wait,notify,notifyAll时,便会抛出java.lang.IllegalMonitorStateException
注意:wait,notify,notifyAll属于Object类的方法,所以所有实例都会有一个wait set。
下面是一个例子:
public class Message {
	//
	private String message = "unknow";

	private boolean flag = false; // if false get, true set

	public synchronized void get() {
		if (flag == true) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("message is " + this.message);
		flag = true;
		notify();
	}

	public synchronized void set(String message) {
		if (flag == false) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.message = message;
		flag = false;
		notify();
	}
}

  注意:上面这个例子中,与wait,notify方法交互的lock都是this对象的对象lock。请注意,如果在synchronized块中使用wait,notify方法,且持有的lock可能不是该程序代码的对象lock,那么必须要像下面这样对相同的对象调用wait,notify方法。
public class Message {
	//
	private String message = "unknow";

	private boolean flag = false; // if false get, true set
	
	private Object lock =  new Object();

	public void get() {
		synchronized (lock) {
			if (flag == true) {
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println("message is " + this.message);
			flag = true;
			lock.notify();
		}
	}

	public void set(String message) {
		synchronized (lock) {
			if (flag == false) {
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			this.message = message;
			flag = false;
			lock.notify();
		}
	}
}

1.10 线程的状态
  线程有6种状态,分别对应着State中的NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TIMED_WAITING。
  1)NEW:创建线程,但还未启动线程,既为调用start方法前得状态。
  2)RUNNABLE:运行状态,正处在jvm运行中,但可能等待操作系统其它资源。
  3)BLOCKED:阻塞状态,例如锁等待
  4)WAITING:等待线程的状态,如调用不带超时值的wait方法时。
  5)TIMED_WAITING:具指定等待时间的某一等待线程的线程状态,如调用sleep方法
  6)TERMINATED:终止线程状态,线程执行完run方法后的状态。

1.11 线程的优先级
  Java的线程优先级是整数,被声明为表示优先级的类字段:
  Thread.MIN_PRIORITY:  优先级为1,最低
  Thread.NORM_PRIORITY: 优先级为5,默认
  Thread.MAX_PRIORITY:  优先级为10,最高
可以通过Thread类实例的setPriority方法,设置优先级,通过getPriority方法获取优先级。
注意,这并不表示OS中的优先级。
  Java规格中几乎没有提及任何关于优先级的规定,除非特别指定Java的执行处理系统,否则优先级就没有任何效果。就某些执行处理系统而言,高优先级的线程会比低优先级的线程先执行或者会分配到更长的CPU时间。
  • 大小: 19.6 KB
  • 大小: 23.3 KB
12
6
分享到:
评论
8 楼 xugangqiang 2013-10-17  
very good
7 楼 fandayrockworld 2011-08-18  
引用
public class Message {
	//
	private String message = "unknow";

	private boolean flag = [color=red]true[/color]; // if false get, true set 此处应为true!

	public synchronized void get() {
		if (flag == true) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("message is " + this.message);
		flag = true;
		notify();
	}

	public synchronized void set(String message) {
		if (flag == false) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.message = message;
		flag = false;
		notify();
	}
}
6 楼 Technoboy 2011-05-20  
slj327 写道
public class MyRunnable implements Runnable{

public void run() {
for(int i = 0; i < 100; i ++){
System.out.println("Java Concurrent Programming");
}
}

public static void main(String[] args) {
new Thread(new MyRunnable()).run();
}
}
这不能启动一个新的线程吧!

引用

start方法是Thread类的方法,如果调用start方法,就会启动新的线程。新启动的线程的操作写在run方法里,启动了新的线程之后,这个线程就会调用run方法。当run方法执行结束,此线程也就结束了。如果我们直接调用new MyThread().run()也是可以的,只是这样不会启动新的线程。

不会启动
5 楼 slj327 2011-05-20  
public class MyRunnable implements Runnable{

public void run() {
for(int i = 0; i < 100; i ++){
System.out.println("Java Concurrent Programming");
}
}

public static void main(String[] args) {
new Thread(new MyRunnable()).run();
}
}
这不能启动一个新的线程吧!
4 楼 Technoboy 2011-05-19  
一江春水邀明月 写道
Java Eye 的风气真差, 什么文章都好多人踩, 投新手。 应该设置个功能, 列出踩脚人的脚印, 让大家链接过去, 看看这些大侠们自己的原创, 为整个社区的知识积累做了什么贡献。

呵呵,浏览量为1,踩为3!
3 楼 一江春水邀明月 2011-05-19  
Java Eye 的风气真差, 什么文章都好多人踩, 投新手。 应该设置个功能, 列出踩脚人的脚印, 让大家链接过去, 看看这些大侠们自己的原创, 为整个社区的知识积累做了什么贡献。
2 楼 Technoboy 2011-05-05  
Summer花的姿态 写道
  

1 楼 Summer花的姿态 2011-05-04  
  

相关推荐

Global site tag (gtag.js) - Google Analytics