乐趣区

关于java:Java-并发编程系列2-线程创建-优先级

  • 学习笔记《深刻了解 Java 虚拟机》
  • 学习笔记《后端架构设计》
  • 学习笔记《Java 基础知识进阶》
  • 学习笔记《Nginx 学习笔记》
  • 学习笔记《前端开发杂记》
  • 学习笔记《设计模式学习笔记》
  • 学习笔记《DevOps 最佳实际指南》
  • 学习笔记《Netty 入门与实战》
  • 学习笔记《高性能 MYSQL》
  • 学习笔记《JavaEE 罕用框架》
  • 学习笔记《Java 并发编程学习笔记》
  • 学习笔记《分布式系统》
  • 学习笔记《数据结构与算法》

Java 人造反对多线程,在 java.lang 包实现了一些对多线程反对的类,Thread 以及 Runnable 接口,Thread 继承了 Runnable 接口。

1、创立线程

能够通过继承 Thread 或者继承 Runable 来实现多线程,应用 start() 办法来启动线程,比方上面的代码

// 继承 Thread,重写 run() 办法
public class MyThread extends Thread {

  @Override
  public void run() {} // 重写 Run 办法
}

// 实现 Runnable 接口
public class MyRunnable implements Runnable {

  @Override
  public void run() {}// 重写 Run 办法
}

应用线程的代码示例

  Thread thread = new MyThread();
  thread.start();

  Thread runnableThread = new Thread(new MyRunnable(), "ThreadName");
  runnableThread.start();
  
  // 因为 Runable 是一种 FunctionalInterface,所以能够应用 Lambda 表达式的形式实现
  // 但实质和 runnableThread 并无区别
  Thread functionStyleThread = new Thread(() -> {}, "functionStyleThread");
  functionStyleThread.start();

手动调用 run() 放和调用一个对象的一般办法没有什么实质上的区别.
启动一个线程,应用 start() 办法,start() 办法是一个原生的 native 办法,其办法签名为 public synchronized void start();   如果线程曾经启动了,再次启动则会抛 llegalThreadStateException  异样

2、线程优先级

上文中创立了两个线程,线程的执行是具备随机性的,由操作系统是否调配工夫片来决定是否执行。咱们能够通过设置操作线程的优先级来设置线程执行的优先级,优先级高的线程调配的工夫片更高,优先级低的线程调配的工夫片少。

计算密集型工作次要耗费大量 CPU 资源,不停进行计算。因为依附 CPU 性能,始终占用 CPU 进行计算,也就说个别状况下可能采纳多任务的数量等于 CPU 外围数。为了避免独占 CPU,所以个别设置为低优先级。

 IO 密集型工作(磁盘读取,web 服务)次要须要 IO 的读取,利用 CPU 的效率较低,大量工夫破费在 IO 上。该种任高优先级比拟适宜。

在创立线程的时候,创立的线程会默认的继承父线程的一些属性,包含线程优先级,是否是守护线程,以及线程组等信息。 线程的优先级并不是默认为 5,而是继承父线程的优先级。 其 init() 办法的局部代码如下:

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    this.name = name;
    Thread parent = currentThread();
    this.group = g; // 设置线程组
    this.daemon = parent.isDaemon(); // 设置是否是守护线程,其值默认等于父线程的属性值
    this.priority = parent.getPriority(); 
    setPriority(priority); // 设置线程优先级,默认值等于父线程的优先级
    
    // 继承父线程的属性
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);


    // 生成线程的 ID,办法签名为 private static synchronized long nextThreadID()
    tid = nextThreadID();}

3、线程优先级生效

在开发的过程中,咱们并不不能依赖线程的优先级,来实现业务,这是因为在不同的操作系统中,对线程的布局有所差别,有的甚至会疏忽线程的优先级, 比方 MacOS 或者 Ubuntu。
上面的代码中,创立了两个线程,一个设置高优先级一个设置低优先级,察看最初的输入后果会发现,两个执行并没有太大的差异,所以线程的优先级在某些操作系统或者某些 JVM 实现上可能有所差别。

private static volatile boolean start = false;

private static volatile boolean end = false;

public static void main(String[] args) throws InterruptedException {PriorityThread runnable1 = new PriorityThread();
  PriorityThread runnable2 = new PriorityThread();

  // 设置线程优先级 
  // MIN_PRIORITY = 0; NORM_PRIORITY=5;  MAX_PRIORITY = 10
  runnable1.setPriority(Thread.MIN_PRIORITY);
  runnable2.setPriority(Thread.MAX_PRIORITY);

  runnable1.start();
  runnable2.start();

  start = true;
  TimeUnit.SECONDS.sleep(2);
  end = true;

  // 输入执行的后果
  System.out.println("count1 =" + runnable1.count);
  System.out.println("count2 =" + runnable2.count);
}

static class PriorityThread extends Thread {

  private Long count = 0L;

  @Override
  public void run() {
    // 没有开始,则始终让出线程执行的工夫片
    while (!start) {Thread.yield();
    }

    // 开始执行,每次执行都让出工夫片
    while (!end) {Thread.yield();
      count++;
    }
  }
}

依据下面的代码, 咱们预估,线程 2 因为优先级高,所以其 count 值应该会比线程 1 高,然而后果却是两者差异并不大。 这因为笔者的操作系统 MacOS,操作系统疏忽了线程的优先级,所以在线程让出工夫片的时候并不会无限调配给高优先级的线程来执行。读者可自定在不同的操作系统尝试下面的代码,察看不同的操作系统是否对线程优先级进行疏忽。

count1 = 5531926
count2 = 5525194
退出移动版