高并发学习笔记(一)
一、并发基础
1.进程与线程
计算机中程序是一个静态的概念,一般对应的是操作系统中的一个可执行文件,当我们运行这个文件,加载到内存当中,开始执行这个程序,就产生了进程。进程则是一个动态的概念,其有如下特点:
-
是程序的一次动态执行过程,拥有特定的内存空间。
-
每个进程都是相互独立的,即使是同一个程序的不同进程之间也是独立的,拥有自己的cpu时间,数据及代码。
线程是由进程产生的,一个进程可以产生多个线程,其特点是:
-
线程是进程内部的一个执行单元,是程序中的一个顺序执行流程。
-
一个进程可以拥有多个线程。
-
进程中的多个线程之间共享堆空间,因此可以访问到相同的变量即对象,还可以进行通信,数据交互及同步操作。但每个线程也有自己独立的空间,即栈空间。
-
线程之间的通信是建立在同用堆空间上,因此不需要额外的通信机制,不仅方便速度也极快。
-
线程的启动,中断,消亡所需资源非常少(相对进程而言)。
进程与线程之间的区别:
-
每个进程都拥有独立的内存及代码,且进程之间的切换开销巨大;而线程则不同,同一进程下的线程共享代码及堆空间,但又有自己独立的栈空间和PC寄存器,且线程之间的切换开销小。
-
进程是资源分配单位,线程则是对进程分配到的资源调度和执行单位。
-
多进程是操作系统中同时运行多个程序;多线程则是同一进程中的多个顺序执行单位。
2.线程的生命周期
了解线程的生命周期之前,我们先看看如何用Java创建新的线程。
/** * 通过继承Thread类重写run方法创建一个线程类 * Created by bzhang on 2019/3/12. */ public class MyThread extends Thread { //线程体,线程启动后的入口方法 @Override public void run() { for (int i=0;i < 10;i++){ System.out.println("当前"+Thread.currentThread().getName()+"循环的次数是:"+i); } } public static void main(String[] args) { Thread thread = new MyThread(); //新建线程 Thread th = new MyThread(); thread.start(); //启动该线程 th.start(); try { Thread.sleep(1000); //主线程休眠1s } catch (InterruptedException e) { e.printStackTrace(); } } }
知道如何创建一个新的线程,接下来看看线程的生命周期。
上图中可以看出一个线程的状态有:新生,就绪,运行,阻塞(锁池和等待队列也属于阻塞状态),死亡五个状态。
1.新生状态:当使用new创建一个新的线程对象时,该线程即处于新生状态。
2.就绪状态:是指线程已经可以执行了,但系统还未分配CPU资源给该线程的状态。一个线程处于就绪状态的原因有:当一个线程对象调用启动该线程的start方法后,该线程即处于就绪状态;解除阻塞状态,即进入就绪状态;一个线程执行过程中调用yield()方法让出CPU资源;JVM主动将CPU资源切换给其它线程。
3.运行状态:一个线程执行自己线程体(run方法)中的代码的状态。运行状态中若因等待其他资源而阻塞或调用某些阻塞的方法就会进入阻塞状态;若线程体重的代码执行完则进入死亡状态;若在系统给定的时间片中未执行结束,则会被系统切换到就绪状态。
4.阻塞状态:是指暂停一个线程的执行以等待某些事件的发生。导致阻生的原因有:调用Thread.sleep()方法(睡眠时间到后自动解除阻塞);调用Object.wait()方法(进入WaitSet队列,需要调用notify()或notifAll()方法来唤醒);调用Thread.join()方法(当前线程等待某个线程终止后,在运行);调用了其他的阻塞方法(如IO阻塞);调用了某个被锁住的资源(进入锁池队列,等待拥有资源的线程使用完后与其他线程竞争资源,竞争到就不阻塞,否则继续阻塞)。
5.死亡状态:是线程最后一个生命阶段,线程处于该阶段的原因只有两个:线程正常执行完线程体中的所有代码;另一个是调用了stop()或destroy()方法(这两个方法已被废弃,不要用)。
3.实现多线程的方式
前面我们已经知道可以通过继承Thread类重写run方法来实现多线程。Java中还提供了了另外两种方法来实现多线程。
1.实现Runable接口:
/** * 通过实现Runable接口来创建新线程 * Created by bzhang on 2019/3/12. */ public class ThreadByRunable implements Runnable { private boolean flag = true; //标记位,当flag为false时结束线程 @Override public void run() { while (flag){ try { Thread.sleep(1000); System.out.println("我是真滴帅!"); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("我gg了!"); } //改变flag状态 public void changeFlag(){ flag = false; } public static void main(String[] args) { ThreadByRunable t = new ThreadByRunable(); //新建线程体t Thread thread = new Thread(t); //根据线程体t创建新线程 thread.start(); //启动新线程 try { Thread.sleep(3000); t.changeFlag(); } catch (InterruptedException e) { e.printStackTrace(); } } }
2.实现Callable接口
/** * 通过实现Callable接口来实现多线程 * Created by bzhang on 2019/3/12. */ public class ThreadByCallable implements Callable<String> { //标志位 private boolean flag = true; //线程体 @Override public String call() throws Exception { int k = 0; while (flag){ Thread.sleep(500); System.out.println("我不够帅吗?"); k++; if (k>3){ changeFlag(); } } System.out.println("我不够帅!"); return "好丑"; } //改变标志位 public void changeFlag(){ flag = false; } public static void main(String[] args) { ThreadByCallable call = new ThreadByCallable(); //新建callable线程体 FutureTask<String> task = new FutureTask(call); //新建一个未来任务,任务的线程体是call new Thread(task).start(); //启动一个线程去执行这个未来任务 try { String res = task.get(); //获得任务的结果 System.out.println(res); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
4.线程类Thread
前面了解了线程,线程的生命周期,如何创建新线程,可以看到Java中线程与Thread类息息相关,但一直不清楚Thread类是干嘛的。下面来看看Thread类:
//实现Runable接口,实现run方法 public class Thread implements Runnable { //线程名称,可在构造时自定义传入,也可由程序自动生成 private volatile String name; //线程的优先级,Java中为每一个新建的线程都设置了一个优先级,优先级用数字1-10表示,若不设置该参数时,线程默认优先级为5。 //优先级越大,优先获取CPU资源的概率越大,但不能保证一定先得到执行,即优先级高只是先执行的概率高。 private int priority; private Thread threadQ; //没用到 private long eetop; //没用到 private boolean single_step; //是否单步执行,没用到 private boolean stillborn = false; //虚拟机状态,没用到 private AccessControlContext inheritedAccessControlContext; //已签名,但永远不会被访问 private long stackSize; //线程请求的堆栈大小,无用 private long nativeParkEventPointer; //无用 //线程是否是守护线程,默认不是守护线程。 //守护线程即在后台运行的线程,如GC,定时任务等 private boolean daemon = false; //线程运行时的线程体 private Runnable target; //当前线程所属的线程组,线程组表示多个线程的集合 private ThreadGroup group; //线程的上下文,即该线程下用加载类和自愿的类加载器 private ClassLoader contextClassLoader; //线程默认的初始化编号,用于线程使用默认名称时的命名 private static int threadInitNumber; //编号自增 private static synchronized int nextThreadNum() { return threadInitNumber++; } //当前线程附属的ThreadLocal ThreadLocal.ThreadLocalMap threadLocals = null; //与此线程相关的InheritableThreadLocal值。 ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; //线程对应的id,唯一 private long tid; //用于生成线程id private static long threadSeqNumber; //用于标记线程状态,默认是线程为启动状态 private volatile int threadStatus = 0; //生成下一个线程id private static synchronized long nextThreadID() { return ++threadSeqNumber; } //中断阻塞器:当线程发生IO中断时,需要在线程被设置为中断状态后调用该对象的interrupt方法 volatile Object parkBlocker; //阻塞器锁,主要用于处理阻塞情况 private volatile Interruptible blocker; //阻断锁 private final Object blockerLock = new Object(); void blockedOn(Interruptible b) { synchronized (blockerLock) { blocker = b; } } //线程优先级最低值 public final static int MIN_PRIORITY = 1; //线程优先级默认值 public final static int NORM_PRIORITY = 5; //线程优先级最大值 public final static int MAX_PRIORITY = 10; //返回当前正在执行的线程对象的引用 public static native Thread currentThread(); //暂停当前正在执行的线程,让出CPU资源去执行其他线程。 //这个方法并不保证其它线程一定等获得CPU资源,有可能当前线程让出后有重新竞争到CPU继续执行。 //这个方法仅是让当前线程回到就绪状态与其他线程一同在重新竞争CPU资源。 public static native void yield(); //使当前正在执行的线程强制休眠一段时间。(毫秒为单位) //线程休眠时,会让出CPU资源,并且在休眠结束之前都不会回到就绪状态,也就是休眠结束前不会去竞争CPU资源 public static native void sleep(long millis) throws InterruptedException; //休眠的重载方法,以纳秒为单位 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); } //重载的初始化方法 private void init(ThreadGroup g, Runnable target, String name,long stackSize) { init(g, target, name, stackSize, null, true); } //线程初始化方法,在创建线程时执行 private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; //初始化线程名称 Thread parent = currentThread(); //父线程 SecurityManager security = System.getSecurityManager(); //获取系统的安全管理器 //若未指定当前线程所属的线程组,就其属于系统默认指定的线程组 if (g == null) { if (security != null) { //若安全管理器不为null(即当前程序使用了安全管理器,使用了安全管理器需要重写getThreadGroup方法),就让线程属于该安全管理器指定的线程组 g = security.getThreadGroup(); } //若未使用安全管理器,则指定父线程所在的线程组为当前线程所属的线程组 if (g == null) { g = parent.getThreadGroup(); } } //确定当前运行的线程是否有权修改此线程组 g.checkAccess(); //安全管理器不为null时,确定是否有访问的权限 if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } } //线程组中未启动的线程数+1 g.addUnstarted(); //指定所属线程组 this.group = g; //指定是否是守护线程,默认保持与父线程一致 this.daemon = parent.isDaemon(); //指定线程的优先级,默认保持与父线程一致 this.priority = parent.getPriority(); //指定线程的上下文对象 if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); //初始化线程的线程体 this.target = target; setPriority(priority); if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); //指定线程堆大小 this.stackSize = stackSize; //线程id tid = nextThreadID(); } //重写Object类的clone方法,线程不允许克隆 @Override protected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } //空构造,使用默认值(包括name,target,id,stackSize)创建一个线程。 public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } //除了线程体外,其他均使用默认值创建一个线程 public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } //该构造方法不是public的,不管 Thread(Runnable target, AccessControlContext acc) { init(null, target, "Thread-" + nextThreadNum(), 0, acc, false); } //依据指定的线程组和线程体创建新线程 public Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0); } //创建名为name的线程 public Thread(String name) { init(null, null, name, 0); } //创建属于group线程组的名为name的线程 public Thread(ThreadGroup group, String name) { init(group, null, name, 0); } //创建名为name线程体为target的新线程 public Thread(Runnable target, String name) { init(null, target, name, 0); } //创建属于group线程组的名为name且线程体为target的新线程 public Thread(ThreadGroup group, Runnable target, String name) { init(group, target, name, 0); } //创建属于group线程组的名为name且线程体为target的新线程,具有指定堆栈大小。别乱用,这个出问题 public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { init(group, target, name, stackSize); } //启动线程,使线程进入就绪状态 public synchronized void start() { //若线程已经起动过,抛异常 if (threadStatus != 0) throw new IllegalThreadStateException(); //将启动的新线程加入到所属的线程组中 group.add(this); boolean started = false; try { start0(); //启动的本地方法,真正启动的方法 started = true; } finally { try { //启动失败就将其从线程组中移除 if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } //启动线程的本地方法 private native void start0(); //执行线程体 @Override public void run() { if (target != null) { target.run(); } } //这个方法是在Run方法执行结束后用于结束线程的。是系统调用的方法,给个在thread退出前,清空该线程的机会 private void exit() { if (group != null) { group.threadTerminated(this); group = null; } target = null; threadLocals = null; inheritableThreadLocals = null; inheritedAccessControlContext = null; blocker = null; uncaughtExceptionHandler = null; } //废弃方法,不管他 @Deprecated public final void stop() { SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if (this != Thread.currentThread()) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise } stop0(new ThreadDeath()); } //废弃方法,不管他 @Deprecated public final synchronized void stop(Throwable obj) { throw new UnsupportedOperationException(); } //线程的中断的方法 //interrupt()对线程的影响与线程的状态和在进行的IO操作有关。 //如果线程在运行中,且没有执行IO操作,interrupt()只是会设置线程的中断标志位,没有任何其它作用。线程应该在运行过程中合适的位置检查中断标志位 //如果一个线程处于了阻塞状态(如线程调用了sleep()、join()、wait()方法),则在线程在检查中断标示时如果发现中断标示为true, //则会在这些阻塞方法调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。 //如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置为true并且该线程将收到一个ClosedByInterruptException异常, //并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。 public void interrupt() { //调用该中断方法的线程不是这个线程本身是要检查权限 if (this != Thread.currentThread()) checkAccess(); //判定当前运行的线程是否有权修改该线程 synchronized (blockerLock) { //同步锁对象blockerLock,中断操作必须同步 Interruptible b = blocker; if (b != null) { interrupt0(); // 本地中断方法 b.interrupt(this); return; } } interrupt0(); } //测试此线程是否已被中断,并清空中断标志位 public static boolean interrupted() { return currentThread().isInterrupted(true); } //测试此线程是否已被中断。此方法不影响线程的中断状态 public boolean isInterrupted() { return isInterrupted(false); } //测试线程是否被中断的本地方法 private native boolean isInterrupted(boolean ClearInterrupted); //废弃不管 @Deprecated public void destroy() { throw new NoSuchMethodError(); } //判断线程是否存活 public final native boolean isAlive(); //废弃不管 @Deprecated public final void suspend() { checkAccess(); suspend0(); } //废弃不管 @Deprecated public final void resume() { checkAccess(); resume0(); } //设置线程的优先级 public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); //检查权限 //设置的优先级不能大于最大值,不能小于最小值 if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } 设置的优先级不能大于线程组的优先级,即最大只能与所在线程组的优先级相同 if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } } //获取该线程测优先级 public final int getPriority() { return priority; } //设置线程名称,要求要同步 public final synchronized void setName(String name) { checkAccess(); if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; if (threadStatus != 0) { setNativeName(name); } } //获取线程名称 public final String getName() { return name; } //获取线程所在的线程组 public final ThreadGroup getThreadGroup() { return group; } //获取当前线程所在线程组中活动线程的数量 public static int activeCount() { return currentThread().getThreadGroup().activeCount(); } //将这个线程组中存活的线程全部复制到预设置好的数组中 public static int enumerate(Thread tarray[]) { return currentThread().getThreadGroup().enumerate(tarray); } //废弃不管 @Deprecated public native int countStackFrames(); //等待mills毫秒的时间,若当前线程在此时间能没有终止,则不继续等待。为0时表示等待时间不限,是个同步方法 public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { //这个分支是无限期等待直到线程结束 //调用了wait(),这个过程的目的是让持有这个同步锁的线程进入等待 //当子线程(即this线程)执行完毕的时候,jvm会自动唤醒阻塞在对象上的线程 while (isAlive()) { wait(0); } } else { //这个分支是等待固定时间,如果线程没结束,那么就不等待了。 while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } //重载join方法 public final synchronized void join(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++; } join(millis); } //重载join 方法 public final void join() throws InterruptedException { join(0); } //将当前线程的堆栈跟踪打印至标准错误流 public static void dumpStack() { new Exception("Stack trace").printStackTrace(); } //设置线程是否为守护线程 public final void setDaemon(boolean on) { checkAccess(); if (isAlive()) { throw new IllegalThreadStateException(); } daemon = on; } //检查线程是否为守护线程 public final boolean isDaemon() { return daemon; } //检查是否允许调用线程修改 thread 参数 public final void checkAccess() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkAccess(this); } } public String toString() { ThreadGroup group = getThreadGroup(); if (group != null) { return "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]"; } else { return "Thread[" + getName() + "," + getPriority() + "," + "" + "]"; } } //获取上下文对象,即类加载器 @CallerSensitive public ClassLoader getContextClassLoader() { if (contextClassLoader == null) return null; SecurityManager sm = System.getSecurityManager(); if (sm != null) { ClassLoader.checkClassLoaderPermission(contextClassLoader, Reflection.getCallerClass()); } return contextClassLoader; } //设置上下文对象 public void setContextClassLoader(ClassLoader cl) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("setContextClassLoader")); } contextClassLoader = cl; } //当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true public static native boolean holdsLock(Object obj); //空的堆栈跟踪元素数组 private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; //返回一个表示该线程堆栈转储的堆栈跟踪元素数组 public StackTraceElement[] getStackTrace() { if (this != Thread.currentThread()) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission( SecurityConstants.GET_STACK_TRACE_PERMISSION); } //线程是否存活 if (!isAlive()) { return EMPTY_STACK_TRACE; } StackTraceElement[][] stackTraceArray = dumpThreads(new Thread[] {this}); StackTraceElement[] stackTrace = stackTraceArray[0]; if (stackTrace == null) { stackTrace = EMPTY_STACK_TRACE; } return stackTrace; } else { return (new Exception()).getStackTrace(); } } //返回所有活动线程的堆栈跟踪的一个映射。 public static Map<Thread, StackTraceElement[]> getAllStackTraces() { // check for getStackTrace permission SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission( SecurityConstants.GET_STACK_TRACE_PERMISSION); security.checkPermission( SecurityConstants.MODIFY_THREADGROUP_PERMISSION); } // Get a snapshot of the list of all threads Thread[] threads = getThreads(); StackTraceElement[][] traces = dumpThreads(threads); Map<Thread, StackTraceElement[]> m = new HashMap<>(threads.length); for (int i = 0; i < threads.length; i++) { StackTraceElement[] stackTrace = traces[i]; if (stackTrace != null) { m.put(threads[i], stackTrace); } // else terminated so we don't put it in the map } return m; } private native static StackTraceElement[][] dumpThreads(Thread[] threads); private native static Thread[] getThreads(); //存放所属线程组中所有活动的线程 //获取线程id public long getId() { return tid; } //线程状态枚举类 public enum State { NEW, //新生状态 RUNNABLE, //运行状态 BLOCKED, //阻塞状态 WAITING, //等待状态 TIMED_WAITING, //也是等待,不过到一定时间会返回 TERMINATED; //终止状态 } //获取线程状态 public State getState() { // get current thread state return sun.misc.VM.toThreadState(threadStatus); } //当 Thread 因未捕获的异常而突然终止时,调用处理程序的接口 @FunctionalInterface public interface UncaughtExceptionHandler { void uncaughtException(Thread t, Throwable e); } //线程由于未捕获到异常而突然终止时调用的处理程序 private volatile UncaughtExceptionHandler uncaughtExceptionHandler; //默认处理程序 private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler; //设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序 public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission( new RuntimePermission("setDefaultUncaughtExceptionHandler") ); } defaultUncaughtExceptionHandler = eh; } //返回线程由于未捕获到异常而突然终止时调用的默认处理程序 public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){ return defaultUncaughtExceptionHandler; } //返回该线程由于未捕获到异常而突然终止时调用的处理程序 public UncaughtExceptionHandler getUncaughtExceptionHandler() { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; } //设置该线程由于未捕获到异常而突然终止时调用的处理程序 public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) { checkAccess(); uncaughtExceptionHandler = eh; } }
线程组概念:是多个线程或线程组的集合,其结构是树。根结点是name=system的线程组。
线程组的特点同一个线程组下的线程可以互相访问获取数据(即线程B与线程C之间可以通信);同一层次下的不同线程组之间的线程时相互隔离的(线程B与线程D之间是隔离的);system线程组是最顶级的线程组,由系统自动创建,任何新建线程组都是其子线程组。
一个新建的线程若不指定所属线程组,则默认其父线程所在的线程组为其所属的线程组(线程M的父线程是线程D,则线程M所属线程组是线程组X)。一个新建线程组若不指定所属线程组也是默认当前创建该线程组的线程所在的线程组为其所属线程组(即在main线程中创建K线程组,则K线程组属于main线程组)。
/** * 线程组测试 * Created by bzhang on 2019/3/13. */ public class ThreadGroupTest { public static void main(String[] args) { Thread thread = Thread.currentThread(); System.out.println(thread.getThreadGroup()); System.out.println(thread.getThreadGroup().getParent()); System.out.println(thread.getThreadGroup().getParent().getParent()); Thread[] threads = new Thread[10]; //获取系统线程组下有多少个活动线程 int enumerate = thread.getThreadGroup().getParent().enumerate(threads); for (Thread t:threads) { System.out.println(t); } } }
其结果如下:
java.lang.ThreadGroup[name=main,maxpri=10] java.lang.ThreadGroup[name=system,maxpri=10] null Thread[Reference Handler,10,system] Thread[Finalizer,8,system] Thread[Signal Dispatcher,9,system] Thread[Attach Listener,5,system] Thread[main,5,main] Thread[Monitor Ctrl-Break,5,main] null null null null
5.Runable接口
一个线程的主要工作就是执行线程体中的代码(run方法),从Thread源码可知Thread的run方法就是执行target线程体的run方法,而target就是一个Runable的实现类对象的引用。
//Thread中run方法 @Override public void run() { if (target != null) { target.run(); } }
@FunctionalInterface public interface Runnable { //Runable实现类必须实现的方法,是新建线程的任务代码 public abstract void run(); }
通过实现Runable接口来新建线程体任务要比继承Thread类重写run方法来新建线程体任务要简单轻松的多,因为大多数时候我们没有要修改Thread中其他的基本行为。
6。Callable接口
已有的两种新建线程的方式都存在一个问题,即线程启动后就与主线程失去联系了,即不通过特殊的方式,主线程对新建线程的运行状况一无所知,而Callable接口则提供了一种交互,其call方法不再像run方法那样返回值为void,也不再不能抛异常了。即Callable可以将线程的执行结果返回给主线程了,而通常主线程是需要这个结果来做下一步的规划的,其使用方法见callable示例。
//V代表返回值的类型 @FunctionalInterface public interface Callable<V> { V call() throws Exception; //执行时若抛异常,主线程也会收到这个异常。 }
- Go语言并发与并行学习笔记(一)
- 学习笔记(九)并发(三)
- Go语言并发与并行学习笔记(一)
- Java并发学习笔记(八)-LinkedBlockingQueue
- Go语言学习笔记-并发
- 并发编程学习笔记之Lock与synchronized
- Java并发学习笔记(15)信号量(Semaphore) 关卡((2)CyclicBarrier)
- Java高并发商城秒杀优化学习笔记
- 学习笔记:java并发编程学习之初识Concurrent
- JAVA并发编程学习笔记之ReentrantLock (r)
- MySQL学习笔记之四:并发控制和事务机制
- Java并发编程学习笔记 深入理解volatile关键字的作用
- 学习JAVA多线程编程 --- 《JAVA多线程编程核心技术》第2章 对象及变量的并发访问 笔记
- WCF学习笔记之并发与限流
- Java并发读书学习笔记(九)——性能与可伸缩性
- 高并发学习笔记(九)
- Java并发读书学习笔记(十)——显式锁
- Java学习笔记—多线程(并发工具类)
- Java并发读书学习笔记(十一)——原子变量与非阻塞同步机制
- 并发编程实战学习笔记(七)——避免活跃性问题