zoukankan      html  css  js  c++  java
  • java lang(Thread) 和 Runable接口

    public
    interface Runnable {
        public abstract void run();
    }
    

     

    public
    class Thread implements Runnable {
         private Runnable target;
    .....
        public Thread() {
            init(null, null, "Thread-" + nextThreadNum(), 0);
        }
        public Thread(Runnable target) {
            init(null, target, "Thread-" + nextThreadNum(), 0);
        }
        Thread(Runnable target, AccessControlContext acc) {
            init(null, target, "Thread-" + nextThreadNum(), 0, acc);
        }
        public Thread(ThreadGroup group, Runnable target) {
            init(group, target, "Thread-" + nextThreadNum(), 0);
        }
        public Thread(String name) {
            init(null, null, name, 0);
        }
    ....
         @Override
        public void run() {
            if (target != null) {
                target.run();
            }
        }

    阻止线程执行

    对于线程的阻止,考虑一下三个方面,不考虑IO阻塞的情况:

    睡眠;

    等待;

    因为需要一个对象的锁定而被阻塞。

    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()是静态方法,只能控制当前正在运行的线程。

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

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

     

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

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

      当线程池中线程都具有相同的优先级,调度程序的JVM实现自由选择它喜欢的线程。这时候调度程序的操作有两种可能:一是选择一个线程运行,直到它阻塞或者运行完成为止。二是时间分片,为池内的每个线程提供均等的运行机会。

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

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

    线程优先级为1~10之间的正整数,JVM从不会改变一个线程的优先级。然而,1~10之间的值是没有保证的。一些JVM可能不能识别10个不同的值,而将这些优先级进行每两个或多个合并,变成少于10个的优先级,则两个或多个优先级的线程可能被映射为一个优先级。

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

    static int MAX_PRIORITY 
              线程可以具有的最高优先级。
    static int MIN_PRIORITY 
              线程可以具有的最低优先级。
    static int NORM_PRIORITY 
              分配给线程的默认优先级。

    3、Thread.yield()方法

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

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

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

     

    小结

    到目前位置,介绍了线程离开运行状态的3种方法:

    1、调用Thread.sleep():使当前线程睡眠至少多少毫秒(尽管它可能在指定的时间之前被中断)。

    2、调用Thread.yield():不能保障太多事情,尽管通常它会让当前运行线程回到可运行性状态,使得有相同优先级的线程有机会执行。

    3、调用join()方法:保证当前线程停止执行,直到该线程所加入的线程完成为止。然而,如果它加入的线程没有存活,则当前线程不需要停止。

     

    除了以上三种方式外,还有下面几种特殊情况可能使线程离开运行状态:

    1、线程的run()方法完成。

    2、在对象上调用wait()方法(不是在线程上调用)。

    3、线程不能在对象上获得锁定,它正试图运行该对象的方法代码。

    4、线程调度程序可以决定将当前运行状态移动到可运行状态,以便让另一个线程获得运行机会,而不需要任何理由。

     

    同步和锁定

    1、锁的原理

    Java中每个对象都有一个内置锁

    当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。

    当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。

    一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。

    释放锁是指持锁线程退出了synchronized同步方法或代码块。

     

    关于锁和同步,有一下几个要点:

    1)、只能同步方法,而不能同步变量和类;

    2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?

    3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。

    4)、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。

    5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。

    6)、线程睡眠时,它所持的任何锁都不会释放。

    7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。

    8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。

    9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。

    三、静态方法同步

     

    要同步静态方法,需要一个用于整个类对象的锁,这个对象是就是这个类(XXX.class)。

    例如:

    public static synchronized int setName(String name){

          Xxx.name = name;

    }

    等价于
    public static int setName(String name){
          synchronized(Xxx.class){
                Xxx.name = name;
          }
    }

    sleep()和wait()的区别

    Java中的多线程是一种抢占式的机制而不是分时机制。线程主要有以下几种状态:可运行,运行,阻塞,死亡。抢占式机制指的是有多个线程处于可运行状态,但是只有一个线程在运行。
           当有多个线程访问共享数据的时候,就需要对线程进行同步。线程中的几个主要方法的比较:
           Thread类的方法:sleep(),yield()等
           Object的方法:wait()和notify()等
           每个对象都有一个机锁来控制同步访问。Synchronized关键字可以和对象的机锁交互,来实现线程的同步。
           由于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁没有被释放,其他线程仍然无法访问这个对象。而wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象。
           Yield()方法是停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield()方法将不会起作用。
           一个线程结束的标志是:run()方法结束。
           一个机锁被释放的标志是:synchronized块或方法结束。
           Wait()方法和notify()方法:当一个线程执行到wait()方法时(线程休眠且释放机锁),它就进入到一个和该对象相关的等待池中,同时失去了对象的机锁。当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中。该线程从锁池中获得机锁,然后回到wait()前的中断现场。
    join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。
    值得注意的是:线程的在被激活后不一定马上就运行,而是进入到可运行线程的队列中。

    共同点: 他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。
    不同点: Thread.sleep(long)可以不在synchronized的块下调用,而且使用Thread.sleep()不会丢失当前线程对任何对象的同步锁(monitor);
                  object.wait(long)必须在synchronized的块下来使用,调用了之后失去对object的monitor, 这样做的好处是它不影响其它的线程对object进行操作。

     

     

     

  • 相关阅读:
    Exaple2_1(显示转换)
    Example2_4(数据的输入Scanner)
    安装jdk遇到的问题
    Java应用程序,用户从键盘只能输入整数,程序输出这些整数的乘积
    Hello.Java//Tom and Jerry
    Example2_3(数据输出System.out.printf)
    Example2_2(基本类型转换)
    c++与java的区别
    大龄屌丝自学笔记Java零基础到菜鸟004
    大龄屌丝自学笔记Java零基础到菜鸟003
  • 原文地址:https://www.cnblogs.com/myseries/p/7482718.html
Copyright © 2011-2022 走看看