多线程的好处无需多言。
这篇日记冠之以Android之名,实际上是Java提供语言级的对多线程程序的设计支持。
一:实现多线程的两种方式:
1:从Thread类继承,并重写run方法。
2:实现Runnable接口,并实现其中的run方法。
二:Java语言对多线的一些需要注意的事项。
1:Java运行时系统实现了一个用于调度线程执行的线程调度器,用于确定某一时刻由哪一个线程在CPU上运行。
2:在java技术中,线程通常是抢占式的而不需要时间片分配进程(分配给每个线程相等的CPU时间的进程)。抢占式调度模型就是许多线程处于可以运行状态(等待状态),但实际上只有一个线程在运行。该线程一直运行到它终止进入可运行状态(等待状态),或者另一个具有更高优先级的线程变成可运行状态。在后一种情况下,低优先级的线程被高优先级的线程抢占,高优先级的线程获得运行的机会。
3:Java线程调度器支持不同优先级线程的抢先方式,但其本身不支持相同优先级线程的时间片轮换。
4:Java运行时系统所在的操作系统(例如:Windows2000)支持时间片的轮换,则线程调度器就支持相同优先级线程的时间片轮换。
5:不可以过度依赖系统的线程调度器,例如:拥有低优先级的线程也有可能获取到执行的机会。
三:示例:
Thread 类:
代码很容易理解,派生类重写了run方法。当然Thread还有许多其他的方法可以重写,这个也是我们派生Thread的一个优势的地方。
Runnable接口:
class TestThread { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); Thread thread=new Thread(mr); //mt.setDaemon(true);//是否后台线程 //mt.setPriority(Thread.MAX_PRIORITY);//设置优先级 mt.start(); //new Thread(mr).start(); } } class MyRunnable implements Runnable { public void run() { while(true) { System.out.println("MyRunnable is running "); } } }
Thread类派生和Runnable接口实现没有很大的区别,一般我们用Runnable接口的比较多,因为Java不支持多继承,当然前提是你不希望重写有关Thread类的方法。
四:线程的同步:
线程的同步的原因就是多个线程访问同一个资源,在资源的访问的过程中出现时间片的切换导致同时多个线程都进入到了资源操作的过程中。也就是说我们需要避免多个线程能同时访问同一个资源。就有像是在商场试衣间试衣服,只能有一个人在一个时间进入试衣间试衣服。多个人拿着衣服,可以理解多个线程在跑。试衣间就是一个大家公共的资源,只能一个人去访问。
简而言之,Java同步的方式有两种:
1:同步块
2:同步方法
这里需要准备一些一些基础知识:
1:每个对象都有一个监视器,或者叫做锁。
2:同步方法是利用this对象的锁。这种一般应用在同步方法。
3:每个类class也有一个锁,也就是这个class所对应的对象的这个所。这个一般用于类的静态方法。
class TestThread { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); new Thread(mr).start(); new Thread(mr).start(); new Thread(mr).start(); new Thread(mr).start(); new Thread(mr).start(); new Thread(mr).start(); } } class MyRunnable implements Runnable { Object o=new Object(); int index=0; public void run() { while(true) { synchronized(o) { System.out.println("Thread ID = "+Thread.currentThread().getName()+"Index ="+index++); } } } }
这样就基本保证了对于大家公共的资源Index变量的访问没有问题。
下面就是利用同步方法的方式,它锁住的对象是this,上面同步块的情况如果把synchronized参数改成this,也就是一样的效果了。
class TestThread { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); new Thread(mr).start(); new Thread(mr).start(); new Thread(mr).start(); new Thread(mr).start(); new Thread(mr).start(); new Thread(mr).start(); } } class MyRunnable implements Runnable { Object o=new Object(); int index=0; public void run() { while(true) { /*synchronized(o) { System.out.println("Thread ID = "+Thread.currentThread().getName()+"Index ="+index++); } */ output(); } } public synchronized void output() { System.out.println("Thread ID = "+Thread.currentThread().getName()+"Index ="+index++); } }
五:线程的死锁。
每一个对象除了有一个锁之外,还有一个等待队列(wait set),当一个对象刚创建的时候,它的对待队列是空的。我们应该在当前线程锁住对象的锁后,去调用该对象的wait方法。当调用对象的notify方法时,将从该对象的等待队列中删除一个任意选择的线程,这个线程将再次成为可运行的线程。当调用对象的notifyAll方法时,将从该对象的等待队列中删除所有等待的线程,这些线程将成为可运行的线程。wait和notify主要用于producer-consumer这种关系中。
class TicketsSystem { public static void main(String[] args) { SellThread st=new SellThread(); new Thread(st).start(); try { Thread.sleep(1); } catch(Exception e) { e.printStackTrace(); } st.b=true; new Thread(st).start(); //new Thread(st).start(); //new Thread(st).start(); } } class SellThread implements Runnable { int tickets=100; Object obj=new Object(); boolean b=false; public void run() { if(b==false) { while(true) sell(); } else { while(true) { synchronized(obj) { try { Thread.sleep(10); } catch(Exception e) { e.printStackTrace(); } synchronized(this) { if(tickets>0) { System.out.println("obj:"+Thread.currentThread().getName()+ " sell tickets:"+tickets); tickets--; } } } } } } public synchronized void sell() { synchronized(obj) { if(tickets>0) { try { Thread.sleep(10); } catch(Exception e) { e.printStackTrace(); } System.out.println("sell():"+Thread.currentThread().getName()+ " sell tickets:"+tickets); tickets--; } } } }
这样基本上Java中多线程的基本概念,我们都接触到了。