• JAVA并行程序基础


    JAVA并行程序基础

    一、有关线程你必须知道的事

    进程与线程

    1. 在等待面向线程设计的计算机结构中,进程是线程的容器。我们都知道,程序是对于指令、数据及其组织形式的描述,而进程是程序的实体。
    2. 线程是轻量级的进程,是程序执行的最小单位。(PS:使用多线程去进行并发程序的设计,是因为线程间的调度和切换成本远小于进程)
    3. 线程的状态(Thread的State类):

      • NEW–刚刚创建的线程,需要调用start()方法来执行线程;
      • RUNNABLE–线程处于执行状态;
      • BLOCKED–线程遇到synchronized同步块,会暂停执行直到获得请求的锁;
      • WAITING–无限制时间的等待;
      • TIMED_WAITING–有限制时间的等待;
      • TERMINATED–执行完毕,表示结束。

    二、线程的基本操作

    创建线程

    代码示例:start()方法会新建线程并调用run()方法!

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

     注意以下代码:这段代码也可以编译执行,但不能新建一个线程,而是在当前线程中调用run()方法。(PS:就是作为一个普通方法来调用)

    Thread t = new Thread();
    t.run();
    
    不能用run()来开启线程,它只会在当前线程中串行执行run()方法。
    

    另外,Thread类有一个非常重要的构造方法:
    public Thread(Runnable target)
    这个构造方法在start()调用时,新线程会执行Runnable.run()方法。实际上,默认的Thread.run()也是这样子做的

    public class MyThread implements Runnable {
        public static void main(String[] args) {
            Thread t = new Thread(new MyThread());
            t.start();
        }
    
        @Override
        public void run() {
            System.out.println("开启新线程执行run()方法");
        }
    }
    
    • 继承Thread的方式定义线程后,就不能在继承其他的类了,导致程序的可扩展性大大降低。而且**重载**run()方法也只是和普通方法一样,不会被JVM主动调用。
    • 使用实现Runnable接口并传入实例给Thread,可以避免重载Thread.run(),单纯使用接口来定义Thread。

    终止线程

    JAVA提供了一个stop()方法来关闭线程,不过这是一个被标注为废弃的方法。原因是stop()方法太过暴力,强行将执行到一半的线程关闭可能导致一些数据不一致的问题。

    一帮情况下,不要使用 stop() 方法!
    

    解决方法:定义一个标记变量stopme,用于指示线程是否需要退出。

    线程中断

    在JAVA中,为了完善线程安全退出,有一种重要的线程协作机制————线程中断。(注意:线程中断会给线程发送一个通知,但线程接到通知后如何处理,则由目标线程自行决定)

    • 线程中断的三个重要方法
        public void Thread.interrupt()                  //中断线程
        public boolean Thread.isInterrupted()           //判断线程是否被中断
        public static boolean Thread.interrupted()      //判断线程是否被中断,并清除当前中断状态
    
        public static boolean Thread.interrupted()      //判断线程是否被中断,并清除当前中断状态
    • 1
    • 2
    • 3
    • Thread.interrupt()方法中断,线程不会立刻停下来,比如死循环体。可以通过isInterrupted()方法来判断是否跳出循环体。

    线程休眠

      1. Thread.sleep()
        让当前线程休眠若干时间,其签名如下:
        public static native void sleep(long millis) throws InterruptedException

        • InterruptedException不是运行时异常,当线程处于休眠状态时,如果被中断就会抛出这个异常。
        • sleep()方法由于中断抛出异常时,会清除中断标记。所以,在异常处理中需要再次设置中断标记!
      2. Thread.wait()与notify()
        这两个方法是配套的,一个线程调用obj.wait()处于等待状态,需要其他线程调用obj.notify()来唤醒。obj对象就是多个线程间有效通信手段

        • Object.wait() 必须包含在 synchronzied 语句中,无论是 wait() 还是 notify() 都需要先获得目标对象的一个监视器。(PS:这两个方法都是执行后立刻释放监视器,防止其他等待对象线程因为该线程休眠而全部无法正常执行)
        • Object.wait() 同样会抛出InterruptedException异常。
        • Object.notifyAll() 方法会唤醒等待队列中所有等待的线程。
      3. suspengd()与resume()
        前者是线程挂起,后者是继续执行,这是一对互相配合的方法。这两个也是已经被废弃的方法,了解一下就行了。

        • suspend() 会导致休眠线程的所有资源都不会被释放,直到执行 resume() 方法。(如果两个方法,后者执行于前者之前,则线程很难有机会被继续执行)
        • 对于被挂起的线程,其状态是Runnable,这会严重影响到我们对于系统当前状态的判断。(不可饶恕)
      4. join()与yield()

        • join() 方法
          其签名如下:
          public final void join() throws InterruptedException;
          public final void join(long millis) throws InterruptedException;
          第一个方法表示无限等待;第二个方法表示一定时间的等待。join()方法会阻塞当前线程,直到目标线程结束,或者到达阻塞时间。
          同时,join()方法的本质是让在当前线程对象实例上调用线程的wait()方法。

        • yield() 方法
          其签名如下:
          public static native void yield();
          这个静态方法会使当前线程让出CPU。(PS:但还是会进行CPU资源的抢夺)

    当某个线程不是那么重要,或者优先级别较低,可以在适当时候调用 Thread.yield(),给予其他重要线程更多工作机会。
    

    关于线程操作的补充

    1. sleep() 与 wait() 的区别
      Object.wait() 和 Thread.sleep() 都可以让线程等待若干时间。除了** wait() 可以被唤醒外,另外一个主要区别:**wait() 方法会释放目标对象的锁,而 sleep() 方法不会释放任何资源

    三、分门别类的管理:线程组

    简单建立一个线程组(代码来源于《实战Java高并发程序设计》书中)

    public class MyThread implements Runnable {
    
        public static void main(String[] args) {
            //创建一个叫"PrintGroup"的线程组
            ThreadGroup tg = new ThreadGroup("PrintGroup");
            Thread t1 = new Thread(tg, new MyThread(), "T1");   //加入线程组
            Thread t2 = new Thread(tg, new MyThread(), "T2");
            t1.start();
            t2.start();
            //由于线程是动态的,activeCount()获得活动线程总数的估算值
            System.out.println("活动线程总数 = " + tg.activeCount());
            //打印出线程组的线程信息
            tg.list();
            //注意这个方法与Thread.stop()方法遇到问题一样
            tg.stop();
        }
    
        @Override
        public void run() {
            String groupAndName = Thread.currentThread().getThreadGroup().getName()
                    + "---" + Thread.currentThread().getName();
            while (true) {
                System.out.println("I am " + groupAndName);
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
    

    四、驻守后台的守护线程

    守护线程,是系统的守护者,在后台完成系统性的服务,比如垃圾回收线程、JIT线程等。当一个Java应用内只有守护线程时,Java虚拟机就会自然退出
    将线程设置为守护线程:

            Thread t = new Thread(new MyThread());
            t.setDaemon(true);
            t.start();
    

    注意:设置守护线程必须在线程start()之前设置,否则会得到一个IllegalThreadStateException,但程序和线程依然可以正常执行,只是线程被当作用户线程而已。

    五、线程优先级

    Java中线程可以有自己的优先级。由于线程的优先级调度和底层操作系统有密切关系,在各个平台表现不一,无法精准控制。
    在Java中,使用 1 到 10 表示线程优先级。一般可以使用三个内置的静态标量表示:

        public final static int MIN_PRIORITY = 1;
        public final static int NORM_PRIORITY = 5;
        public final static int MAX_PRIORITY = 10;
    

    PS:数字越大,优先级越高(一般情况)。

    六、synchronized 关键字

    synchronized 关键字用法简单记录:

    • 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
    • 直接作用于实例对象:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
    • 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。

    synchronized 可以保证线程间的原子性、可见性和有序性(被 synchronized 限制的多个线程是串行执行的)。

    七、部分隐秘的错误记录

    • 执行(synchronized)同步代码的两个线程指向了不同的Runnable实例,即两个线程使用了两个不同的锁。解决方法:将同步代码修改为静态(static)方法。
    • 使用线程不安全的容器,如 ArrayList、HashMap 等。解决方法:改用线程安全的容器,如 Vector、ConcurrentHashMap。
    • 不变对象加锁,导致对于临界区代码控制出现问题。
      • 不变对象:对象一旦被创建,就不能修改。
        (如,Integer i; i++的本质是创建一个新的Integer对象,并将引用赋值给i,同时这里涉及到Integer引用的特性)

    八、参考资料

      • 《实战Java高并发程序设计》(葛一鸣 郭超 著)
  • 相关阅读:
    06 is和==的区别 encode()编码 decode()解码
    05 dic的增删改查 字典的嵌套 考试题dic.get()的相关使用
    03 编码 int ,bool,str的常用操作 主要讲str
    01 基本数据类型 变量 if语句
    04 列表的增删改查 常用方法 元祖 range
    02 while循环 格式化输出 运算符
    多校2 Harmonious Army hdu6598 网络流
    P3159 [CQOI2012]交换棋子 网络流
    P2172 [国家集训队]部落战争 最大流
    P2402 奶牛隐藏 网络流
  • 原文地址:https://www.cnblogs.com/MaxElephant/p/8109483.html
Copyright © 2020-2023  润新知