如何实现一个线程
在jdk1.5发布之前,要写一个线程主要有以下两种方式:
1、实现Runnable接口的run()方法,然后把该类的实例作为Thread构造函数的参数.
2、继承Thread类,重写run()方法。
线程同步
在jdk1.5发布之前,实现线程同步(安全)的方法有以下三种:
1、使用synchronized关键字
2、使用volatile关键字
3、wait()和notify()、notifyAll()
volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
synchronized可以用来修饰类方法(static方法)、实例方法和对象。
synchronized关键字用来标识同步的方法或者同步的代码块,要执行同步的方法或者同步的代码块首先必须先取得对应的锁,如果锁已经被其他对象获取则线程会阻塞自己来等待锁。
运行类级别(static方法)的同步方法需要取得类锁,运行实例的同步方法需要取得对象级别的锁。如果不是同步方法,则不需要获得锁,sychronized修饰的方法不会阻塞没有synchronized修饰的方法。
类锁和对象锁是两个不同的锁,其中一个锁定不会影响另外一个。synchronized修饰的类方法不能阻塞synchronized修饰的实例方法,反之亦然。
synchronized修饰类方法
public class StaticSync {
public static void main(String [] args)
{
new StaticSyncThread1().start();
new StaticSyncThread2().start();
new ShowInterval().start();
}
public synchronized static void staticMethod1()
{
try {
System.out.println("in staticMethod1");
Thread.sleep(3000);
System.out.println("exit staticMethod1");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public synchronized static void staticMethod2()
{
try {
System.out.println("in staticMethod2");
Thread.sleep(1000);
System.out.println("exit staticMethod2");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class StaticSyncThread1 extends Thread{
public void run(){
System.out.println("线程1开始运行");
StaticSync.staticMethod1();
}
}
class StaticSyncThread2 extends Thread{
public void run(){
System.out.println("线程2开始运行");
StaticSync.staticMethod2();
}
}
/**
* 为了使阻塞的效果能从console的输出中一眼看出来而
* 增加的辅助类:每隔一秒打印一个数字
*/
class ShowInterval extends Thread{
public void run(){
for(int i=0;i<6;i++)
{
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
输出结果:
线程1开始运行
线程2开始运行
in staticMethod1
0
1
2
exit staticMethod1
in staticMethod2
3
exit staticMethod2
4
5
结论:线程1运行方法staticMethod1时,线程2由于不能进入staticMethod2方法而被阻塞,直到线程1退出staticMethod1。
synchronized修饰类方法
public class InstanceSync {
public static void main(String[] args) {
InstanceSync is = new InstanceSync();
new InstanceSyncThread1(is).start();
new InstanceSyncThread2(is).start();
new InstanceSyncThread3(is).start();
new ShowInstanceInterval().start();
}
public synchronized void instanceMethod1() {
try {
System.out.println("in instanceMethod1");
Thread.sleep(3000);
System.out.println("exit instanceMethod1");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public synchronized void instanceMethod2() {
try {
System.out.println("in instanceMethod2");
Thread.sleep(1000);
System.out.println("exit instanceMethod2");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void instanceMethod3() {
synchronized (this) {
try {
System.out.println("in instanceMethod3");
Thread.sleep(5000);
System.out.println("exit instanceMethod3");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void instanceMethod4()
{
try {
System.out.println("in instanceMethod3");
System.out.println("exit instanceMethod3");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class InstanceSyncThread1 extends Thread {
private InstanceSync is;
public InstanceSyncThread1(InstanceSync is) {
this.is = is;
}
public void run() {
System.out.println("线程1开始运行");
is.instanceMethod1();
}
}
class InstanceSyncThread2 extends Thread {
private InstanceSync is;
public InstanceSyncThread2(InstanceSync is) {
this.is = is;
}
public void run() {
System.out.println("线程2开始运行");
is.instanceMethod2();
}
}
class InstanceSyncThread3 extends Thread {
private InstanceSync is;
public InstanceSyncThread3(InstanceSync is) {
this.is = is;
}
public void run() {
System.out.println("线程3开始运行");
is.instanceMethod3();
}
}
/**
* 为了使阻塞的效果能从console的输出中一眼看出来而 增加的辅助类:每隔一秒打印一个数字
*/
class ShowInstanceInterval extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
输出结果就不贴了...
synchronized修饰对象
如下是synchronized修饰对象的代码片段,如果synchronized{}中的代码要运行,那就必须得先取得is对象的对象锁。如果锁得不到就得阻塞自己等待锁。如果取得了is的对象锁则其他需要得到is对象锁才能运行的代码段,实例方法(synchronized修饰的实例方法)的线程则需要阻塞自己。
synchronized (is) {
try {
System.out.println("in objectSyncMethod1");
Thread.sleep(5000);
System.out.println("exit objectSyncMethod1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
wait()和notify()、notifyAll()
这三个方法是java.lang.Object类声明的方法,也就是说任何对象都有这么几个方法。看一下API对这几个方法的说明。
wait() -----在其他线程调用此对象的 notify()
方法或notifyAll()
方法前,导致当前线程等待. 需要先得到对象的锁(比如使用synchronized(obj))才能在对象上调用该方法。调用wait()方法会释放对象锁.
notify() -----唤醒在此对象监视器上等待的单个线程。需要得到对象的锁才能调用该方法.
notifyAll() -----唤醒在此对象监视器上等待的所有线程。需要得到对象的锁才能调用该方法.
正确的使用这几个方法能实现更复杂的线程同步。
Sleep()
Thread类的静态方法, 调用该方法导致当前线程挂起. 可指定挂起的时间. 如果sleep不指定休眠时间或者指定的休眠时间为0,则当前线程释放cpu回到就绪状态,再和其他等待线程一起竞争cpu. 在休眠期间不会释放对象锁.
Thread对象上几个过时的方法说明
1、stop() -----释放所有资源并终止当前线程。会破坏临界资源的一致性。
2、suspend()和resume() ------suspend()不释放任何资源,阻塞线程。该方法会造成死锁。resume()与suspend()结对使用,唇亡齿寒。
3、destroy() ------终止线程但不释放任何资源,显然不合理,容易造成死锁。
线程中断之interrupt方法
interrupt()并不直接中断线程,而是设定一个中断标志,然后由程序进行中断检查,决定是否中断.
sleep(),wait(),join()等方法之所以会抛出中断异常,是因为在这些方法的实现中不断去检查中断标志,然后抛出中断异常.
先调用interrupt(),再调用上述方法同样会抛出中断异常.