栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

浅谈多线程_让程序更高效的运行

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

浅谈多线程_让程序更高效的运行

Java Thread 的一些认识:

Java是抢占式线程,一个线程就是进程中单一的顺序控制流,单个进程可以拥有多个并发任务,其底层是切分CPU时间,多线程和多任务往往是使用多处理器系统的最合理方式

进程可以看作一个程序或者一个应用;线程是进程中执行的一个任务,多个线程可以共享资源

一个Java 应用从main 方法开始运行,main 运行在一个线程内,也被称为 “主线程”,Runnable也可以理解为Task (任务)

JVM启动后,会创建一些守护线程来进行自身的常规管理(垃圾回收,终结处理),以及一个运行main函数的主线程

随着硬件水平的提高,多线程能使系统的运行效率得到大幅度的提高,同时异步操作也增加复杂度和各种并发问题

■ 线程 VS 进程

在一个已有进程中创建一个新线程比创建一个新进程快的多

终止一个线程比终止一个进程快的多

同一个进程内线程间切换比进程间切换更快

线程提供了不同的执行程序间通信的效率,同一个进程中的线程共享同一进程内存和文件,无序调用内核就可以互相通信,而进程间通信必须通过内核

■ 同步和异步

同步方法一旦开始,调用者必须等到方法调用返回之后,才能继续后续行为

无先后顺序,一旦开始,方法调用便立即返回,调用者就可以继续后续行为,一般为另一个线程执行

■ 阻塞和非阻塞

当一个线程占用临界区资源,其他线程也想要使用该资源就必须等待,等待会导致线程的挂起,也就是阻塞(线程变成阻塞状态)。

此时若占用资源的线程一直不愿意释放资源,那么其他所有阻塞在该临界区的线程都会被挂起,变成阻塞状态,不能正常工作,直到占用线程释放资源

非阻塞强调没有一个线程可以妨碍其他线程执行,所有线程都会尝试去做下一步工作

■ 临界资源与临界区

一般指的是公共共享资源,即可以被多个线程共享使用。但同一时间只能由一个线程去访问和操作临界区的资源,一旦临界区资源被一个线程占用,其他线程也想要使用该资源就必须等待,

就好比好多人想上大号,但只有一个坑,一个人占了坑,其他人就得排队等待喽

临界区可以认为是一段代码,线程会在该端代码中访问共享资源,因此临界区的界定标准就是是否访问共享(临界)资源(有点类似形成闭包的概念);一次只允许有一个程序(进程/线程)在该临界区中

■ 类定义

public class Thread implements Runnable {
  
 private static native void registerNatives();
 static {
  registerNatives();
 }

■ 构造器


public Thread() {
 init(null, null, "Thread-" + nextThreadNum(), 0);
}

public Thread(Runnable target) {
 init(null, target, "Thread-" + nextThreadNum(), 0);
}

public Thread(ThreadGroup group, Runnable target) {
 init(group, target, "Thread-" + nextThreadNum(), 0);
}

public Thread(String name) {
 init(null, null, name, 0);
}

public Thread(ThreadGroup group, String name) {
 init(group, null, name, 0);
}

public Thread(Runnable target, String name) {
 init(null, target, name, 0);
}

public Thread(ThreadGroup group, Runnable target, String name) {
 init(group, target, name, 0);
}
 
public Thread(ThreadGroup group, Runnable target, String name,long stackSize) {
 init(group, target, name, stackSize);
}

■ 重要变量

//线程名,用char来保存(String底层实现就是char)
private char  name[];
//线程优先级
private int   priority;
//不明觉厉
private Thread  threadQ;
//不明觉厉
private long  eetop;

private boolean  single_step;

private boolean  daemon = false;

private boolean  stillborn = false;

private Runnable target;

private ThreadGroup group;

private ClassLoader contextClassLoader;

private AccessControlContext inheritedAccessControlContext;

private static int threadInitNumber;

ThreadLocal.ThreadLocalMap threadLocals = null;

 ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

private long stackSize;

private long nativeParkEventPointer;

private long tid;

private static long threadSeqNumber;

private volatile int threadStatus = 0;

volatile Object parkBlocker;

private volatile Interruptible blocker;
//阻塞器锁,主要用于处理阻塞情况
private final Object blockerLock = new Object();
 
public final static int MIN_PRIORITY = 1;

public final static int NORM_PRIORITY = 5;

public final static int MAX_PRIORITY = 10;

private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION =
   new RuntimePermission("enableContextClassLoaderOverride");
// null unless explicitly set 线程异常处理器,只对当前线程有效
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
// null unless explicitly set 默认线程异常处理器,对所有线程有效
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;

■ 本地方法


private static native void registerNatives();
static {
 registerNatives();
}

public static native void yield();

public static native void sleep(long millis) throws InterruptedException;

public final native boolean isAlive();

private native boolean isInterrupted(boolean ClearInterrupted);

public static native Thread currentThread();
public static native boolean holdsLock(Object obj);
private native void start0();
private native void setPriority0(int newPriority);
private native void stop0(Object o);
private native void suspend0();
private native void resume0();
private native void interrupt0();
private native void setNativeName(String name);

■ 线程初始化


private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
 if (name == null) {
  throw new NullPointerException("name cannot be null");
 }
 //返回当前线程,即创建该hread的线程 currentThread是个本地方法
 Thread parent = currentThread();
 //安全管理器根据Java安全策略文件决定将哪组权限授予类
 //如果想让应用使用安全管理器和安全策略,可在启动JVM时设定-Djava.security.manager选项
 //还可以同时指定安全策略文件
 //如果在应用中启用了Java安全管理器,却没有指定安全策略文件,那么Java安全管理器将使用默认的安全策略
 //它们是由位于目录$JAVA_HOME/jre/lib/security中的java.policy定义的
 SecurityManager security = System.getSecurityManager();
 if (g == null) {
  
  
  if (security != null) {
   g = security.getThreadGroup();
  }
  
  if (g == null) {
   g = parent.getThreadGroup();
  }
 }
 
 //判断当前运行线程是否有变更其线程组的权限
 g.checkAccess();
 if (security != null) {
  if (isCCLOverridden(getClass())) {
   security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
  }
 }
 //新建线程数量计数+1 或者说未就绪线程数+1==> nUnstartedThreads++;
 g.addUnstarted();
 this.group = g;
 this.daemon = parent.isDaemon();//若当前运行线程是守护线程,新建线程也是守护线程
 this.priority = parent.getPriority();//默认使用当前运行线程的优先级
 this.name = name.toCharArray();
 //设置contextClassLoader
 if (security == null || isCCLOverridden(parent.getClass()))
  this.contextClassLoader = parent.getContextClassLoader();
 else
  this.contextClassLoader = parent.contextClassLoader;
 this.inheritedAccessControlContext = AccessController.getContext();
 this.target = target;
 setPriority(priority);//若有指定的优先级,使用指定的优先级
 if (parent.inheritableThreadLocals != null)
  this.inheritableThreadLocals =
   ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
 
 this.stackSize = stackSize;
 
 tid = nextThreadID();
}
private static synchronized long nextThreadID() {
 return ++threadSeqNumber;
}

■ start 方法


public synchronized void start() {
 
 if (threadStatus != 0)
  throw new IllegalThreadStateException();
 
 //通知所属线程组该线程已经是就绪状态,因而可以被添加到该线程组中
 //同时线程组的未就绪线程数需要-1,对应init中的+1
 group.add(this);
 boolean started = false;
 try {
  //调用本地方法,将内存中的线程状态变更为就绪态
  //同时JVM会立即调用run方法,获取到CPU之后,线程变成运行态并立即执行run方法
  start0();
  started = true;//标记为已开启
 } finally {
  try {
   if (!started) {
    group.threadStartFailed(this);//如果变更失败,要回滚线程和线程组状态
   }
  } catch (Throwable ignore) {
   
   //如果start0出错,会被调用栈直接通过
  }
 }
}
-------------
//start之后会立即调用run方法
Thread t = new Thread( new Runnable() {
 @Override
 public void run() {
  System.out.println(Thread.currentThread().getName());
 }
},"roman");
t.start(); //roman

■ run 方法


@Override
public void run() {
 if (target != null) {
  target.run();
 }
}

■ isAlive 方法


public final native boolean isAlive();

✺ 线程运行 : 模拟电梯运行类

public class LiftOff implements Runnable {
 private int countDown = 10; //电梯阶层
// private static int taskCount = 0;
// private final int id = taskCount++;

 public LiftOff(){
 }

 // syn countDown
 private synchronized int getCountDown(){
  --countDown;
  return countDown;
 }

 // 获取电梯状态
 public String status() {
  return Thread.currentThread().toString()+ "("+
    (countDown > 0? countDown: "Liftoff!") + "),";
 }

 @Override
 public void run(){
  while (getCountDown() >0){
   System.out.println(status());
   Thread.yield();
  }
 }

 public static void main(String[] args) {
  // thread's start()
  Thread thread = new Thread(new LiftOff());
  thread.start(); // 调用 run()
  System.out.println("================Waiting for LiftOff...===========================");
 }

}

线程都会有自己的名字

获取线程对象的方法: Thread.currentThread()

目标 run() 结束后线程完成

JVM线程调度程序决定实际运行哪个处于可运行状态的线程

使用线程池执行处理任务

线程状态流程图:

■ sleep 方法


public static void sleep(long millis, int nanos) throws InterruptedException {
 if (millis < 0) {
  throw new IllegalArgumentException("timeout value is negative");
 }
 if (nanos < 0 || nanos > 999999) {
  throw new IllegalArgumentException("nanosecond timeout value out of range");
 }
 //换算用
 if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
  millis++;
 }
 sleep(millis);
}

public static native void sleep(long millis) throws InterruptedException;

■ yield 方法


public static native void yield();

■ interrupt 方法


public void interrupt() {
 if (this != Thread.currentThread())
  checkAccess();
 synchronized (blockerLock) {
  Interruptible b = blocker;
  if (b != null) {
   // Just to set the interrupt flag
   // 调用interrupt方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程!
   interrupt0();   
   b.interrupt(this);
   return;
  }
 }
 interrupt0();
}

■ Daemon

分类:在JAVA中分成两种线程:用户线程和守护线程

特性:当进程中不存在非守护线程时,则全部的守护线程会自动化销毁

应用: JVM在启动后会生成一系列守护线程,最有名的当属GC(垃圾回收器)

Thread t2 = new Thread(new Runnable() {
 @Override
 public void run() {
  System.out.println("守护线程运行了");
  for (int i = 0; i < 500000;i++){
   System.out.println("守护线程计数:" + i);
  }
 }
}, "kira");
t2.setDaemon(true);
t2.start();
Thread.sleep(500);
-------------
//输出:
......
守护线程计数:113755
守护线程计数:113756
守护线程计数:113757
守护线程计数:113758
//结束打印:会发现守护线程并没有打印500000次,因为主线程已经结束运行了

■ wait 和 notify 机制

wait()使线程停止运行,notify()使停止的线程继续运行

使用wait()、notify()、notifyAll()需要先对调用对象加锁,即只能在同步方法或同步块中调用这些方法

调用wait()方法后,线程状态由RUNNING变成WAITING,并将当前线程放入对象的等待队列中

调用notify()或notifyAll()方法之后,等待线程不会从wait()返回,需要notify()方法所在同步块代码执行完毕而释放锁之后,等待线程才可以获取到该对象锁并从wait()返回

notify()方法将随机选择一个等待线程从等待队列中移到同步队列中;notifyAll()方法会将等待队列中的所有等待线线程全部移到同步队列中,被移动线程状态由WAITING变成BLOCKED

// wait/notify 简单实例
public class NumberPrint implements Runnable {
 private int number;
 public byte[] res;
 public static int count = 5;

 public NumberPrint(int number, byte a[]){
  this.number = number;
  res = a;
 }

 @Override
 public void run() {
  synchronized (res){
   while (count-- > 0){
    try {
     res.notify(); //唤醒等待res资源的线程,把锁交给线程(该同步锁执行完毕自动释放锁)
     System.out.println(" " + number);
     res.wait(); //释放CPU控制权,释放res的锁,本线程阻塞,等待被唤醒
     System.out.println("----------线程"+Thread.currentThread().getName() + "获得锁,wait()后的代码继续运行:"+ number);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   } //end of while
   return;
  } //syn
 }

 public static void main(String[] args) {
  final byte[] a = {0}; //以该对象为共享资源
  new Thread(new NumberPrint(1,a),"1").start();
  new Thread(new NumberPrint(2,a),"2").start();
 }
}

*****各位看客,由于对线程的调度机制还理解比较浅,所以本文会持续更新…… ********

以上这篇浅谈多线程_让程序更高效的运行就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持考高分网。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/143746.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号