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进行操作。

     

     

     

  • 相关阅读:
    Can't remove netstandard folder from output path (.net standard)
    website项目的reference问题
    The type exists in both DLLs
    git常用配置
    Map dependencies with code maps
    How to check HTML version of any website
    Bootstrap UI 编辑器
    网上职位要求对照
    Use of implicitly declared global variable
    ResolveUrl in external JavaScript file in asp.net project
  • 原文地址:https://www.cnblogs.com/myseries/p/7482718.html
Copyright © 2011-2022 走看看