zoukankan      html  css  js  c++  java
  • JAVA线程

    线程:
            一. 从进程到线程
                进程是指运行中的应用程序,每一个进程都有自己独立的内存空间。一个应用程序可以同时启动多个进程。
                线程是指进程中的一个执行流程。一个进程可以由多个线程组成,在一个进程中可以同时运行多个不同的线程,它们分别执行不同的任务。
                当进程内的多个线程同时运行时,这种运行方式称为并发运行。


                线程与进程的区别:每个进程都需要操作系统为其分配独立的内存地址空间,而同一进程中的所有线程在同一块地址空间中工作,这些线程可以共享同一块内存和系统资源。


            二. java中的线程
                在java虚拟机进程中,执行程序代码的任务是由线程来完成的。每当用java命令启动一个Java虚拟机进程时,Java虚拟机都 会创建一个主线程。该线程从程序入口main()方法开始执行。


                java中可以把线程分为前台线程(执行线程)、后台线程(守护线程)。


            三. 线程的创建和启动
                Java虚拟机的主线程从启动类的main()方法开始运行。此外,用户还可以创建自己的线程,它将和主线程并发运行。创建线程有两种方式:


                . 继承java.lang.Thread类; //extends
                . 实现Runnable接口;//implements


                1.  继承java.lang.Thread类
                    1)重写Thread类的run()方法;//public void run(){}——包含线程运行时所执行的代码;
                    2)在main()方法中调用start()方法启动线程,一个线程只能被启动一次。
    ------------------------------------------------------------------------------------------
    例:
    public class ThreadTest extends Thread {
       public void run(){
      System.out.println("thread");
       }
       public static void main(String[] args) {
    ThreadTest t=new ThreadTest();
    t.start();
       }
    }
    ------------------------------------------------------------------------------------------


                2.  实现Runnable接口
                    Java只能继承一个类,如果继承了Thread类,就不能继承其他类了。因此可以通过实现java.lang.Runnable接口避免此问题。
    1)实现Runnable接口
    2)创建Thread对象,传入要启动的线程类
    3)调用Thread对象的start()方法  //接口中没有start()方法,不能直接启动
    ------------------------------------------------------------------------------------------
    例:
    public class ThreadTest implements Runnable {
       public void run(){
      System.out.println("thread");
       }
       public static void main(String[] args) {
    ThreadTest tt=new ThreadTest();
    Thread td=new Thread(tt);
    td.start();
       }
    }
    ------------------------------------------------------------------------------------------




            四. 线程状态


                线程的五种状态;


                1. 新建(New)——用new刚创建出的线程对象,只被分配了内存
                2. 就绪(Runnable)——调用start()方法后,位于可运行池,等待CPU的使用权。
                3. 运行(Running)——占用CPU,执行程序代码。
                4. 阻塞(Blocked)——线程因为某些原因放弃CPU,暂时停止运行。

                   阻塞状态可分为三种:
                   1)位于对象等待池中的阻塞状态(Blocked in objects' wait pool): 运行状态时,执行某个对象的wait()方法;
                   2)位于对象锁池中的阻塞状态(Blocked in object's lock pool): 当线程处于运行状态,试图获得某个对象的同步锁时,如该对象的同步锁已经被其他线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中;
                   3)其他阻塞状态(Otherwise Blocked): 当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O
                     请求时,就会进入这个状态。
                   
      线程从阻塞状态只能进入就绪状态,然后才有机会转到运行状态。


                5. 死亡(Dead)——线程执行完run()方法或执行期间遇到异常退出run()方法。


            五. 线程调度

       按照特定的机制为多个线程分配CPU的使用权。
       
    有两种调度模型:
         . 分时调度模型:让所有线程轮流获得CPU的使用权,并且平均分配每个线程占用CPU的时间片。
                 . 抢占式调度模型:优先让可运行池中优先级高的线程较多可能占用CPU(概率高),如果可运行池中线程的优先级相同,那么就随机选择一个线程,使其占用CPU。处于可运行状态的线程会一直运行,直至它不得不放弃CPU。Java虚拟机采用这种。


                一个线程会因为以下原因而放弃CPU: 
                . Java虚拟机让当前线程暂时放弃CPU,转到就绪状态;(interrupt)
                . 当前线程因为某些原因而进入阻塞状态; (sleep   join)
                . 线程运行结束;


                线程的调度还依赖于操作系统。在某些操作系统中,只要运行中的线程没有阻塞, 就不会放弃CPU;在某些操作系统中,即使运行中的线程没有遇到阻塞,也会在运行一段时间后放弃CPU,给其他线程运行机会。


                常用方法:
                

                1.  stop();

                     可以强制终止一个线程,但从JDK1.2开始废弃了stop()方法。在实际编程中,一般是在受控制的线程中定义一个标志变量,其他线程通过改变标志变量的值,来控制线程的自然终止、暂停及恢复运行。



                2. isAlive(); 

                    判定某个线程是否是活着的(该线程如果处于可运行状态、运行状态和阻塞状态、对象等待队列和对象的锁池中返回true)



                3. Thread.sleep(5000);//参数单位为毫秒 

                    放弃CPU, 转到阻塞状态。当结束睡眠后,首先转到就绪状态,如有其它线程在运行,不一定运行,而是在可运行池中等待获得CPU。

                    线程在睡眠时如果被中断,就会收到一个InterrupedException异常,线程跳到异常处理代码块。


                4. void sleepingThread.interrupt():
                    中断某个线程


                5. boolean otherThread.isInterrupted():
                    测试某个线程是否被中断,与static boolean  interrupted()不同,对它的调用不会改变该线程的“中断”状态。


       
                6. public void join();
                    public void join(long timeout);
                    挂起当前线程,直至它所调用的线程终止才被运行。
                    线程A中调用线程B.join(),是使A线程阻塞,B线程开始执行。
                    谁调用谁阻塞。


            六. 线程的同步


          多个线程在操纵共享资源——实例变量时,有可能引起共享资源的况争。为了保证每个线程能正常执行操作,保证共享资源能正常访问和修改。Java引入了同步进制,具体做法是在有可能引起共享资源竞争的代码前加上synchronized标记。这样的代码被称为同步代码块。


           每个Java对象都有且只有一个同步锁,在任何时刻,最多只允许一个线程拥有这把锁。当一个线程试图执行带有synchronized标记的代码块时,该线程必须首先获得this关键字引用的对象的锁。
                   如果这个锁已经被其他线程占用,Java虚拟机就会把这个线程放到this指定对象的锁池中,线程进入阻塞状态。在对象的锁池中可能会有许多等待锁的线程。等到其他线程释放了锁,Java虚拟机会从锁池中随机取出一个线程,使这个线程拥有锁,并且转到就绪状态。

                   假如这个锁没有被其他线程占用,线程就会获得这把锁,开始执行同步代码块。在一般情况下,线程只有执行完同步代码块,才会释放锁,使得其他线程能够获得锁。


                    如果一个方法中的所有代码都属于同步代码,则可以直接在方法前用synchronized修饰。


               public synchronized String pop(){...}
          等价于
                public String pop(){
                       synchronized(this){...}
                }


                线程同步的特征:


                1. 如果一个同步代码块和非同步代码块同时操纵共享资源,仍然会造成对共享资源的竞争。
                    因为当一个线程执行一个对象的同步代码块时,其他线程仍然可以执行对象的非同步代码块。
                2. 每个对象都有唯一的同步锁。
                3. 在静态方法前面也可以使用synchronized修饰符。此时该同步锁的对象为类对象(类的Class对象)。
        4. 当一个线程开始执行同步代码块时,并不意味着必须以不中断的方式运行。进入同步代码块的线程也可以执行
                   Thread.sleep()或者执行Thread.yield()方法,此时它并没有释放锁,只是把运行机会(即CPU)让给了其他的线程。
                5. synchnozied声明不会被继承。


        同步是解决共享资源竞争的有效手段。当一个线程已经在操纵共享资源时,其他共享线程只能等待。为了提升并发性能,应该使同步代码块中包含尽可能少的操作,使得一个线程能尽快释放锁,减少其他线程等待锁的时间。




            七. 线程的通信



                锁对象.wait(): 执行该方法的线程释放对象的锁,Java虚拟机把该线程放到该对象的     等待池中。该线程等待其它线程将它唤醒;
                锁对象.notify(): 执行该方法的线程唤醒在对象的等待池中等待的一个线程。Java虚拟机从对象的等待池中随机选择一个线程,把它转到对象的锁池中。如果对象的等待池中没有任何线程,那么notify()方法什么也不做。
                锁对象.notifyAll():会把对象的等待池中的所有线程都转到对象的锁池中。
       注意:notify notifyAll只会唤醒等待池中等待同一个锁对象的线程,因为同一个时刻在等到池中可能会有多个线程,而这多个线程可能是在等待不同的锁对象。

                假如t1线程和t2线程共同操纵一个s对象,这两个线程可以通过s对象的wait()和notify()方法来进行通信。通信流程如下:


                1. 当t1线程执行对象s的一个同步代码块时,t1线程持有对象s的锁,t2线程在对象s的锁池中等待;
                2. t1线程在同步代码块中执行s.wait()方法, t1释放对象s的锁,进入对象s的等待池;
                3. 在对象s的锁池中等待锁的t2线程获得了对象s的锁,执行对象s的另一个同步代码块;
                4. t2线程在同步代码块中执行s.notify()方法,Java虚拟机把t1线程从对象s的等待池移到对象s的锁池中,在那里等待获得锁。
                5. t2线程执行完同步代码块,释放锁。t1线程获得锁,继续执行同步代码块。




            八. 线程的死锁


                A线程等待B线程持有的锁,而B线程正在等待A持有的锁;
    --------------------------------------------------------------------------------------------------
    死锁的例子:
    package t;


    public class deadlock extends Thread {
      public static String x="x";
      public static String y="y";
      int a;
      public deadlock(int a){
     this.a=a;
      }
      public void run(){
     if(a==1){
     synchronized(x){
     System.out.println("1 in x");
     //sleep(1000);
     synchronized(y){
     System.out.println("1 in y");
     }
     }
     }
     if(a==2){
     synchronized(y){
     System.out.println("2 in y");
     //sleep(1000);
     synchronized(x){
     System.out.println("2 in x");
     }
     }
     }
      }
      public static void main(String[] args) {
    deadlock d1=new  deadlock(1);
    deadlock d2=new  deadlock(2);
    d1.start();
    d2.start();
      }
    }
    //大概率会死锁,也可能不会锁。加上sleep后则一定会死锁,但需要捕获异常InterruptedException e。
    --------------------------------------------------------------------------------------------------


            九. 线程让步


                Thread.yield()静态方法,如果此时具有相同优先级的其他线程处于就绪状态,那么yield()方法将把当前运行的线程放到可运行池中并使另一个线程运行。如果没有相同优先级的可运行线程,则yield()方法什么也不做。


                sleep()和yield()方法都是Thread类的静态方法,都会使当前处于运行状态的线程放弃CPU,把运行机会让给别的线程。区别:


                . sleep()不考虑其他线程优先级;
                  yield()只会给相同优先级或者更高优先级的线程一个运行的机会。
                . sleep()转到阻塞状态;
        yield()转到就绪状态;
                . sleep()会抛出InterruptedException异常,
                  yield()不抛任何异常
                . sleep()比yield方法具有更好的可移植性,yield()只在测试时用。


            十. 调整线程优先级
       
    注意:优先级高的线程只能获得较多运行的概率,但是实际中不一定真的有效果 
            Thread类的setPriority(int)和getPriority()方法分别用来设置优先级和读取优先级。优先级用整数来表示,取值范围是1-10,Thread类有以下3个静态常量。


                . MAX_PRIORITY: 10, 最高;
                . MIN_PRIORITY: 1, 最低;
                . NORM_PRIORITY: 5, 默认优先级;


       其它:stop():         中止线程运行;            已过时
                  resume():    使暂停线程恢复运行; 已过时
                  suspend():  暂停线程,不释放锁; 已过时


                释放对象的锁:


                . 执行完同步代码块;
                . 执行同步代码块过程中,遇到异常而导致线程终止,释放锁;
                . 执行同步代码块过程中,执行了锁所属对象的wait()方法,释放锁进入对象的等待池;


                线程不释放锁:


                . Thread.sleep()方法,放弃CPU,进入阻塞状态;
                . Thread.yield()方法,放弃CPU,进入就绪状态;
                . suspend()方法,暂停当前线程,已过时;

    -------------------------------------------------------------------------------------------------

    总结:

     *线程Thread调用的常用方法:isAlive,sleep,interrupt,isInterrupted,join,yield。(stop,suspend,resume)

     *锁对象synchronized调用的方法:wait,notify,notifyAll。
    -------------------------------------------------------------------------------------------------
  • 相关阅读:
    《大话数据结构》第1章 数据结构绪论 1.2 你数据结构怎么学的?
    伍迷七八月新浪微博集锦
    《大话数据结构》第9章 排序 9.7 堆排序(下)
    《大话数据结构》第3章 线性表 3.8.2 单链表的删除
    《大话数据结构》第9章 排序 9.5 直接插入排序
    《大话数据结构》第9章 排序 9.8 归并排序(上)
    《大话数据结构》第2章 算法基础 2.9 算法的时间复杂度
    《大话数据结构》第1章 数据结构绪论 1.1 开场白
    《大话数据结构》第9章 排序 9.1 开场白
    [AWS] Assign a public IP address to an EC2 instance after launched
  • 原文地址:https://www.cnblogs.com/codeToSuccess/p/13906270.html
Copyright © 2011-2022 走看看