<span style="font-size:14px;">class SalerThread extends Thread{ private int sum = 5; public void sale(){ System.out.println(getName() + "号窗口正在售票,还剩" + (--sum) + "张票."); } @Override public void run(){ while (sum > 0) { sale(); } } } public class TestThread { public static void main(String[] args) { SalerThread thread = new SalerThread(); SalerThread thread1 = new SalerThread(); SalerThread thread2 = new SalerThread(); thread.setName("1"); thread1.setName("2"); thread2.setName("3"); thread.start(); thread1.start(); thread2.start(); } } 我们可以看到结果,很明显不符合我们的实际情况,但是但我们换成实现Runnable呢! class SalerRunnale implements Runnable{ private int sum = 5; public void sale(){ System.out.println(Thread.currentThread().getName() + "号窗口正在售票,还剩" + (--sum) + "张票."); } @Override public void run() { while (sum > 0) { sale(); } } } public class TestThread { public static void main(String[] args) { SalerRunnale runnale = new SalerRunnale(); Thread thread = new Thread(runnale, "1"); Thread thread1 = new Thread(runnale, "1"); Thread thread2 = new Thread(runnale, "1"); thread.start(); thread1.start(); thread2.start(); } }</span>
也许你想问了,为什么我们是调用start()呢,我们当然也可以调用run()啦,我保证,编译器不会报错,而且有结果,但是,这个结果是按照我们调用所谓的线程的顺序输出的。
也许你会想,哼,肯定只是偶然,但当我们多次调用之后,结果依然不变。好了,其实,在直接调用run()方法时,jvm只是把它当做一个普通方法进行调用,并不会给他重新分配一个线程。所以一定要记住,在写新线程的时候,调用的是它的start()方法,而不是我们重写的run()方法。
好了,现在聊一聊多线程的一些状态:好了,到这,我们还需要说一下线程的中断。线程在执行完run方法最后一条语句后,或者有未捕获的异常时,线程会终止。当然,我们可以调用interrupt方法来终止线程。每个线程都有表示中断状态的boolean值。每个线程都应该不时检查这个标志位,当然,假如在调用它之前,线程已经调用了wait,sleep或者join,他的interrupt
status将会被清除,并且会抛出一个InterruptedException
.异常。
Thread还有一个join方法,在一个线程A中调用另一个线程B,线程B调用join,那么线程A将等待线程B执行完毕。或者调用join的重载方法,设置时间表示之多等待多久。
<span style="font-size:14px;"><span style="font-size:12px;">class RunnableA implements Runnable{ @Override public void run() { for (int i = 0; i < 5; i++){ System.out.println("threadA :" + i); } } } public class TestForJoin { public static void main(String[] args) throws InterruptedException { RunnableA runnableA = new RunnableA(); Thread threadA = new Thread(runnableA); threadA.start(); // threadA.join(); for (int i = 0; i < 5; i++){ System.out.println(Thread.currentThread().getName() + " :" + i); } } }</span></span>当我们注释threadA.join();时,结果如图:
当我们取消注释:
假如是独立的两个线程,一个线程调用join,对另外一个线程是不起作用的。
<span style="font-size:14px;"><span style="font-size:12px;">public class TestForJoin { public static void main(String[] args) throws InterruptedException { RunnableA runnableA = new RunnableA(); Thread threadA = new Thread(runnableA, "ThreadA"); Thread ThreadB = new Thread(runnableA, "ThreadB"); threadA.start(); threadB.start(); threadA.join(); for (int i = 0; i < 5; i++){ System.out.println(Thread.currentThread().getName() + " :" + i); } } }</span></span>因为在一个线程A中调用另一个线程B,B线程调用join,这只会阻塞A线程,假如同时存在线程C,C依旧可以和B抢占CPU.
我们说过线程之间的资源出自身的一些变量方法,其余都共享,所以,多线程必定会产生多个线程同时修改一块内存块的问题。我们该如何避免呢。
最简单也是最有效的方法当然是不使用多线程啦。但是当我们必须使用多线程的时候我们该怎么办。
我们先来做一个小程序,多线程按顺序打印1-9,要求先打印1-3,在打印4-6,最后全部输出。
<span style="font-size:14px;"><span style="font-size:12px;">public class ThreadPrint { static Lock lock = new ReentrantLock(); static Condition reachThree = lock.newCondition(); static Condition reachSix = lock.newCondition(); public static void main(String[] args) { final int[] integer = {0}; Thread threadA = new Thread(new Runnable() { @Override public void run() { lock.lock(); try { for (int i = 1; i <= 3; i++) { System.out.println(i); } integer[0] = 3; reachThree.signal(); } finally { lock.unlock(); } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { lock.lock(); try { while (integer[0] != 3) { reachThree.await(); } for (int i = 4; i <= 6; i++) { System.out.println(i); } integer[0] = 6; reachSix.signal(); } catch (InterruptedException e) { } finally { lock.unlock(); } } }); Thread threadC = new Thread(new Runnable() { @Override public void run() { lock.lock(); try { while (integer[0] != 6) { reachSix.await(); } for (int i = 6; i <= 9; i++) { System.out.println(i); } integer[0] = 9; } catch (InterruptedException e) { } finally { lock.unlock(); } } }); threadA.start(); threadB.start(); threadC.start(); } }</span></span>
Lock是concurrent中给我们提供的一种锁机制,当一个线程进入一个类时,便给它加上锁,当还有其他的线程在之前线程还未退出,请求范文该类时,就会被阻塞,直至第一个进入的线程退出并解除锁。condition是一个条件,一个锁可以有一个或多个条件。当线程因为一个条件被阻塞时,会自动释放它的锁,一边其他线程进入并唤醒它。我们调用的锁ReentrantLock是一种可重入的锁,但一个线程进入一个类,调用一个含锁的方法时,它的计数器会加1,当释放一个锁时便减一,每次有其他线程进入该类会检查计数器是否为0.我们还可以在调用构造函数时将它设置成公平锁new ReentrantLock(true),即等待时间越长越有可能获得该锁,但是它的效率相比不公平锁会低很多,所以不是必须使用它,我们总是使用缺省的不公平锁。
当然还有一种ReentrantReadWriteLock,当线程频繁对数据进行读操作时,他是一个很好的选择,它有读锁和写锁,它允许多个线程进行读取,指定数量的线程进行写操作。所以效率高。每次进行读操作时利用ReentrantReadWriteLock.ReadLock,写操作便用ReentrantReadWriteLock。
当然java还提供一种更为简单的机制synchronized,可以设置同步块和同步方法,具体可以参考我的另一篇博文:初探java 对象中wait(),notify(),notifyAll() 和线程中的synchronized。需要多说的是,synchronized调用的实际上是一个内部锁,它只含有一个条件。
你会想那什么时候用synchronized什么时候用Lock呢。
1.其实最好是两个都不用,因为在我们的concurrent包中,有同步队列,他会自动帮我们处理数据的同步问题。
2.如果synchronized能够符合我们要求时,尽量使用它。
3.当我们需要用到Lock/Condition的特殊功能时,才考虑使用它。
原文地址:http://blog.csdn.net/u010233260/article/details/44954659