多线程技术博客二

来源:互联网 发布:arm linux gcc安装包 编辑:程序博客网 时间:2024/04/26 05:18

                         多线程技术博客二

1、睡眠

Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。

线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。

 

睡眠的实现:调用静态方法。

    

    try {            Thread.sleep(123);        } catch (InterruptedException e) {            e.printStackTrace();          }


 

睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。

 

 

注意:

1、线程睡眠是帮助所有线程获得运行机会的最好方法。

2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。

3、sleep()是静态方法,只能控制当前正在运行的线程。

 

2、线程的优先级和线程让步yield()

线程的让步是通过Thread.yield()来实现的。yield()方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。 

要理解yield(),必须了解线程的优先级的概念。线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。

   注意:当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。 

设置线程的优先级:线程默认的优先级是创建它的执行线程的优先级。可以通过setPriority(int newPriority)更改线程的优先级。例如:

     

   Thread t = new MyThread();        t.setPriority(8);        t.start();

 

线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:

static int MAX_PRIORITY
          线程可以具有的最高优先级。


static int MIN_PRIORITY
          线程可以具有的最低优先级。

static int NORM_PRIORITY
          分配给线程的默认优先级。

3、Thread.yield()方法

   Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。

yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

/** * Java线程:线程的调度-让步 * * @author leizhimin 2009-11-4 9:02:40 */ public class Test {         public static void main(String[] args) {                 Thread t1 = new MyThread1();                 Thread t2 = new Thread(new MyRunnable());                 t2.start();                 t1.start();         } } class MyThread1 extends Thread {         public void run() {                 for (int i = 0; i < 10; i++) {                         System.out.println("线程1第" + i + "次执行!");                 }         } } class MyRunnable implements Runnable {         public void run() {                 for (int i = 0; i < 10; i++) {                         System.out.println("线程2第" + i + "次执行!");                         Thread.yield();                 }         } }


 

4、join()方法 

Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。例如:

        Thread t = new MyThread();
        t.start();
        t.join();

 

5、线程安全类

当一个类已经很好的同步以保护它的数据时,这个类就称为“线程安全的”。即使是线程安全类,也应该特别小心,因为操作的线程实际上仍然不一定安全。举个形象的例子,比如一个集合是线程安全的,有两个线程在操作同一个集合对象,当第一个线程查询集合非空后,删除集合中所有元素的时候。第二个线程也来执行与第一个线程相同的操作,也许在第一个线程查询后,第二个线程也查询出集合非空,但是当第一个执行清除后,第二个再执行删除显然是不对的,因为此时集合已经为空了。看个代码: public class NameList {     private List nameList = Collections.synchronizedList(new LinkedList());     public void add(String name) {         nameList.add(name);     }     public String removeFirst() {         if (nameList.size() > 0) {             return (String) nameList.remove(0);         } else {             return null;         }     } } public class Test {     public static void main(String[] args) {         final NameList nl = new NameList();         nl.add("aaa");         class NameDropper extends Thread{             public void run(){                 String name = nl.removeFirst();                 System.out.println(name);             }         }         Thread t1 = new NameDropper();         Thread t2 = new NameDropper();         t1.start();         t2.start();     } }


 

6、线程死锁

死锁对Java程序来说,是很复杂的,也很难发现问题。当两个线程被阻塞,每个线程在等待另一个线程时就发生死锁。

   还是看一个比较直观的死锁例子:

 

public class DeadlockRisk {     private static class Resource {         public int value;     }     private Resource resourceA = new Resource();     private Resource resourceB = new Resource();     public int read() {         synchronized (resourceA) {             synchronized (resourceB) {                 return resourceB.value + resourceA.value;             }         }     }     public void write(int a, int b) {         synchronized (resourceB) {             synchronized (resourceA) {                 resourceA.value = a;                 resourceB.value = b;             }         }     } } 

假设read()方法由一个线程启动,write()方法由另外一个线程启动。读线程将拥有resourceA锁,写线程将拥有resourceB锁,两者都坚持等待的话就出现死锁。

 

实际上,上面这个例子发生死锁的概率很小。因为在代码内的某个点,CPU必须从读线程切换到写线程,所以,死锁基本上不能发生。