精华内容
下载资源
问答
  • Java Thread 的一些认识:Java是抢占式线程,一个线程就是进程中单一的顺序控制流,单个进程可以拥有多个并发任务,其底层是切分CPU时间,多线程和多任务往往是使用多处理器系统的最合理方式进程可以看作一个程序...

    Java Thread 的一些认识:

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

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

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

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

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

    0dfa70fe78a5756f183a2ee8f0350e32.png

    ■ 线程 VS 进程

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

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

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

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

    ■ 同步和异步

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

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

    ■ 阻塞和非阻塞

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

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

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

    ■ 临界资源与临界区

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

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

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

    ■ 类定义

    public class Thread implements Runnable {

    /* Make sure registerNatives is the first thing does.

    初始化时调用 Java 本地方法,实现了Runnable接口

    */

    private static native void registerNatives();

    static {

    registerNatives();

    }

    ■ 构造器

    /**

    * 默认构造器

    * 其中name规则为 "Thread-" + nextThreadNum()

    */

    public Thread() {

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

    }

    /**

    * 创建一个指定Runnable的线程

    * 其中name规则为 "Thread-" + nextThreadNum()

    */

    public Thread(Runnable target) {

    init(null, target, "Thread-" + nextThreadNum(), 0);

    }

    /**

    * 创建一个指定所属线程组和Runnable的线程

    * 其中name规则为 "Thread-" + nextThreadNum()

    */

    public Thread(ThreadGroup group, Runnable target) {

    init(group, target, "Thread-" + nextThreadNum(), 0);

    }

    /**

    * 创建一个指定name线程

    */

    public Thread(String name) {

    init(null, null, name, 0);

    }

    /**

    * 创建一个指定所属线程组和name的线程

    */

    public Thread(ThreadGroup group, String name) {

    init(group, null, name, 0);

    }

    /**

    * 创建一个指定Runnable和name的线程

    */

    public Thread(Runnable target, String name) {

    init(null, target, name, 0);

    }

    /**

    * Allocates a new {@code Thread} object so that it has {@code target}

    * as its run object, has the specified {@code name} as its name,

    * and belongs to the thread group referred to by {@code group}.

    * 创建一个新的Thread对象,同时满足以下条件:

    * 1.该线程拥有一个指定的Runnable对象用于方法执行

    * 2.该线程具有一个指定的名称

    * 3.该线程属于一个指定的线程组ThreadGroup

    *

    If there is a security manager, its

    * {@link SecurityManager#checkAccess(ThreadGroup) checkAccess}

    * method is invoked with the ThreadGroup as its argument.

    * 若这里有个安全管理器,则ThreadGroup将调用checkAccess方法进而触发SecurityManager的checkAccess方法

    *

    In addition, its {@code checkPermission} method is invoked with

    * the {@code RuntimePermission("enableContextClassLoaderOverride")}

    * permission when invoked directly or indirectly by the constructor of a subclass which

    * overrides the {@code getContextClassLoader} or {@code setContextClassLoader} methods.

    * 当enableContextClassLoaderOverride被开启时,checkPermission将被重写子类直接或间接地调用

    *

    The priority of the newly created thread is set equal to the

    * priority of the thread creating it, that is, the currently running

    * thread. The method {@linkplain #setPriority setPriority} may be

    * used to change the priority to a new value.

    * 新创建的Thread的优先级等同于创建它的线程的优先级,调用setPriority会变更其优先级

    *

    The newly created thread is initially marked as being a daemon

    * thread if and only if the thread creating it is currently marked

    * as a daemon thread. The method {@linkplain #setDaemon setDaemon}

    * may be used to change whether or not a thread is a daemon.

    * 当且仅当线程创建时被显示地标记为守护线程,新创建的线程才会被初始化为一个守护线程

    * setDaemon方法可以设置当前线程是否为守护线程

    */

    public Thread(ThreadGroup group, Runnable target, String name) {

    init(group, target, name, 0);

    }

    /**

    * Allocates a new {@code Thread} object so that it has {@code target} as its run object,

    * has the specified {@code name} as its name, and belongs to the thread group referred to

    * by {@code group}, and has the specified stack size.

    * 创建一个新的Thread对象,同时满足以下条件:

    * 1.该线程拥有一个指定的Runnable对象用于方法执行

    * 2.该线程具有一个指定的名称

    * 3.该线程属于一个指定的线程组ThreadGroup

    * 4.该线程拥有一个指定的栈容量

    *

    This constructor is identical to {@link #Thread(ThreadGroup,Runnable,String)}

    * with the exception of the fact that it allows the thread stack size to be specified.

    * The stack size is the approximate number of bytes of address space that the virtual machine

    * is to allocate for this thread's stack. The effect of the {@code stackSize} parameter,

    * if any, is highly platform dependent.

    * 栈容量指的是JVM分配给该线程的栈的地址(内存)空间大小,这个参数的效果高度依赖于JVM运行平台

    *

    On some platforms, specifying a higher value for the {@code stackSize} parameter may allow

    * a thread to achieve greater recursion depth before throwing a {@link StackOverflowError}.

    * Similarly, specifying a lower value may allow a greater number of threads to exist

    * concurrently without throwing an {@link OutOfMemoryError} (or other internal error).

    * The details of the relationship between the value of the stackSize parameter

    * and the maximum recursion depth and concurrency level are platform-dependent.

    * On some platforms, the value of the {@code stackSize} parameter

    * may have no effect whatsoever.

    * 在一些平台上,栈容量越高,(会在栈溢出之前)允许线程完成更深的递归(换句话说就是栈空间更深)

    * 同理,若栈容量越小,(在抛出内存溢出之前)允许同时存在更多的线程数

    * 对于栈容量、最大递归深度和并发水平之间的关系依赖于平台

    *

    The virtual machine is free to treat the {@code stackSize} parameter as a suggestion.

    * If the specified value is unreasonably low for the platform,the virtual machine may instead

    * use some platform-specific minimum value; if the specified value is unreasonably high,

    * the virtual machine may instead use some platform-specific maximum.

    * Likewise, the virtual machine is free to round the specified value up or down as it sees fit

    * (or to ignore it completely).

    * JVM会将指定的栈容量作为一个参考依据,但当小于平台最小值时会直接使用最小值,最大值同理

    * 同样,JVM会动态调整栈空间的大小以适应程序的运行或者甚至直接就忽视该值的设置

    *

    Due to the platform-dependent nature of the behavior of this constructor, extreme care

    * should be exercised in its use.The thread stack size necessary to perform a given computation

    * will likely vary from one JRE implementation to another. In light of this variation,

    * careful tuning of the stack size parameter may be required,and the tuning may need to

    * be repeated for each JRE implementation on which an application is to run.

    * 简单总结一下就是:这个值严重依赖平台,所以要谨慎使用,多做测试验证

    * @param group

    * the thread group. If {@code null} and there is a security

    * manager, the group is determined by {@linkplain

    * SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.

    * If there is not a security manager or {@code

    * SecurityManager.getThreadGroup()} returns {@code null}, the group

    * is set to the current thread's thread group.

    * 当线程组为null同时有个安全管理器,该线程组由SecurityManager.getThreadGroup()决定

    * 当没有安全管理器或getThreadGroup为空,该线程组即为创建该线程的线程所属的线程组

    * @param target

    * the object whose {@code run} method is invoked when this thread

    * is started. If {@code null}, this thread's run method is invoked.

    * 若该值为null,将直接调用该线程的run方法(等同于一个空方法)

    * @param name

    * the name of the new thread

    * @param stackSize

    * the desired stack size for the new thread, or zero to indicate

    * that this parameter is to be ignored.

    * 当栈容量被设置为0时,JVM就会忽略该值的设置

    * @throws SecurityException

    * if the current thread cannot create a thread in the specified thread group

    * 如果当前线程在一个指定的线程组中不能创建一个新的线程时将抛出 安全异常

    * @since 1.4

    */

    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;

    /* Whether or not to single_step this thread. 不明觉厉*/

    private boolean single_step;

    /* Whether or not the thread is a daemon thread. 是否是守护线程,默认非守护线程*/

    private boolean daemon = false;

    /* JVM state. 是否一出生就领便当,默认false*/

    private boolean stillborn = false;

    /* What will be run. Thread的run方法最终会调用target的run方法*/

    private Runnable target;

    /* The group of this thread. 当前线程的所属线程组*/

    private ThreadGroup group;

    /* The context ClassLoader for this thread 当前线程的ClassLoader*/

    private ClassLoader contextClassLoader;

    /* The inherited AccessControlContext of this thread 当前线程继承的AccessControlContext*/

    private AccessControlContext inheritedAccessControlContext;

    /* For autonumbering anonymous threads. 给匿名线程自动编号,并按编号起名字*/

    private static int threadInitNumber;

    /* ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class.

    * 当前线程附属的ThreadLocal,而ThreadLocalMap会被ThreadLocal维护(ThreadLocal会专门分析)

    */

    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*

    * InheritableThreadLocal values pertaining to this thread. This map is

    * maintained by the InheritableThreadLocal class.

    * 主要作用:为子线程提供从父线程那里继承的值

    * 在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值

    * 创建一个线程时如果保存了所有 InheritableThreadLocal 对象的值,那么这些值也将自动传递给子线程

    * 如果一个子线程调用 InheritableThreadLocal 的 get() ,那么它将与它的父线程看到同一个对象

    */

    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    /*

    * The requested stack size for this thread, or 0 if the creator did not specify a stack size.

    * It is up to the VM to do whatever it likes with this number; some VMs will ignore it.

    * 栈容量:当设置为0时,JVM会忽略该值;该值严重依赖于JVM平台,有些VM甚至会直接忽视该值

    * 该值越大,线程栈空间变大,允许的并发线程数就越少;该值越小,线程栈空间变小,允许的并发线程数就越多

    */

    private long stackSize;

    /* JVM-private state that persists after native thread termination.*/

    private long nativeParkEventPointer;

    /* Thread ID. 每个线程都有专属ID,但名字可能重复*/

    private long tid;

    /* For generating thread ID 用于ID生成,每次+1*/

    private static long threadSeqNumber;

    /*

    * Java thread status for tools,initialized to indicate thread 'not yet started'

    * 线程状态 0仅表示已创建

    */

    private volatile int threadStatus = 0;

    /**

    * The argument supplied to the current call to java.util.concurrent.locks.LockSupport.park.

    * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker

    * Accessed using java.util.concurrent.locks.LockSupport.getBlocker

    * 主要是提供给 java.util.concurrent.locks.LockSupport该类使用

    */

    volatile Object parkBlocker;

    /* The object in which this thread is blocked in an interruptible I/O operation, if any.

    * The blocker's interrupt method should be invoked after setting this thread's interrupt status.

    * 中断阻塞器:当线程发生IO中断时,需要在线程被设置为中断状态后调用该对象的interrupt方法

    */

    private volatile Interruptible blocker;

    //阻塞器锁,主要用于处理阻塞情况

    private final Object blockerLock = new Object();

    /* The minimum priority that a thread can have. 最小优先级*/

    public final static int MIN_PRIORITY = 1;

    /* The default priority that is assigned to a thread. 默认优先级*/

    public final static int NORM_PRIORITY = 5;

    /* For generating thread ID 最大优先级*/

    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;

    ■ 本地方法

    /*

    * Make sure registerNatives is the first thing does.

    * 确保clinit最先调用该方法:所有该方法是类中的最靠前的一个静态方法

    * clinit:在JVM第一次加载class文件时调用,用于静态变量初始化语句和静态块的执行

    * 所有的类变量初始化语句和类型的静态初始化语句都被Java编译器收集到该方法中

    *

    * registerNatives方法被native修饰,即是本地方法,将由C/C++去完成,并被编译成了.dll,供JAVA调用

    * 其主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦

    */

    private static native void registerNatives();

    static {

    registerNatives();

    }

    /** 主动让出CPU资源,当时可能又立即抢到资源 **/

    public static native void yield();

    /** 休眠一段时间,让出资源但是并不会释放对象锁 **/

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

    /** 检查 线程是否存活 **/

    public final native boolean isAlive();

    /** 检查线程是否中断 isInterrupted() 内部使用 **/

    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);

    ■ 线程初始化

    /**

    * Initializes a Thread.

    * 初始化一个线程

    * @param g the Thread group

    * @param target the object whose run() method gets called

    * @param name the name of the new Thread

    * @param stackSize the desired stack size for the new thread, or

    * zero to indicate that this parameter is to be ignored.

    */

    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) {

    /* Determine if it's an applet or not */

    /* If there is a security manager, ask the security manager what to do. */

    if (security != null) {

    g = security.getThreadGroup();

    }

    /* If the security doesn't have a strong opinion of the matter use the parent thread group. */

    if (g == null) {

    g = parent.getThreadGroup();

    }

    }

    /* checkAccess regardless of whether or not threadgroup is explicitly passed in. */

    //判断当前运行线程是否有变更其线程组的权限

    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);

    /* Stash the specified stack size in case the VM cares */

    this.stackSize = stackSize;

    /* Set thread ID 线程安全,序列号每次同步+1*/

    tid = nextThreadID();

    }

    private static synchronized long nextThreadID() {

    return ++threadSeqNumber;

    }

    ■ start 方法

    /**

    * Causes this thread to begin execution; the Java Virtual Machine

    * calls the run method of this thread.

    * 线程进入就绪态,随后JVM将会调用这个线程run方法

    * 当获取到CPU时间片时,会立即执行run方法,此时线程会直接变成运行态

    *

    * The result is that two threads are running concurrently: the

    * current thread (which returns from the call to the

    * start method) and the other thread (which executes its

    * run method).

    *

    * It is never legal to start a thread more than once.

    * In particular, a thread may not be restarted once it has completed execution.

    * 一个线程只能被start一次,特别是线程不会在执行完毕后重新start

    * 当线程已经start了,再次执行会抛出IllegalThreadStateException异常

    * @exception IllegalThreadStateException if the thread was already started.

    * @see #run()

    * @see #stop()

    */

    public synchronized void start() {

    /**

    * This method is not invoked for the main method thread or "system"

    * group threads created/set up by the VM. Any new functionality added

    * to this method in the future may have to also be added to the VM.

    * 该方法不会被主线程或系统线程组调用,若未来有新增功能,也会被添加到VM中

    * A zero status value corresponds to state "NEW".

    * 0对应"已创建"状态 -> 用常量或枚举标识多好

    */

    if (threadStatus != 0)

    throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started

    * so that it can be added to the group's list of threads

    * and the group's unstarted count can be decremented. */

    //通知所属线程组该线程已经是就绪状态,因而可以被添加到该线程组中

    //同时线程组的未就绪线程数需要-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) {

    /* do nothing. If start0 threw a Throwable then

    it will be passed up the call stack */

    //如果start0出错,会被调用栈直接通过

    }

    }

    }

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

    //start之后会立即调用run方法

    Thread t = new Thread( new Runnable() {

    @Override

    public void run() {

    System.out.println(Thread.currentThread().getName());

    }

    },"roman");

    t.start(); //roman

    ■ run 方法

    /**

    * If this thread was constructed using a separate Runnable run object,

    * then that Runnable object's run method is called;

    * otherwise, this method does nothing and returns.

    * Subclasses of Thread should override this method.

    * 若Thread初始化时有指定Runnable就执行其的run方法,否则doNothing

    * 该方法必须被子类实现

    */

    @Override

    public void run() {

    if (target != null) {

    target.run();

    }

    }

    ■ isAlive 方法

    /**

    * Tests if this thread is alive. A thread is alive if it has

    * been started and has not yet died.

    * 测试线程是否处于活动状态

    * 活动状态:线程处于正在运行或者准备开始运行状态

    * @return true if this thread is alive;

    * false otherwise.

    */

    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线程调度程序决定实际运行哪个处于可运行状态的线程

    使用线程池执行处理任务

    线程状态流程图:

    19c00aa22e7370de6e50a1ac20caba7c.png

    ■ sleep 方法

    /**

    * Causes the currently executing thread to sleep (temporarily cease execution)

    * for the specified number of milliseconds plus the specified number of nanoseconds,

    * subject to the precision and accuracy of system timers and schedulers.

    * The thread does not lose ownership of any monitors.

    * 使线程睡眠一段毫秒时间,但线程并不会丢失已有的任何监视器

    */

    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);

    }

    /** 我们一般会直接调用native方法,这或许是我们主动使用的最多次的native方法了 **/

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

    ■ yield 方法

    /**

    * A hint to the scheduler that the current thread is willing to yield

    * its current use of a processor. The scheduler is free to ignore this hint.

    * 暗示线程调度器当前线程将释放自己当前占用的CPU资源

    * 线程调度器会自由选择是否忽视此暗示

    *

    Yield is a heuristic attempt to improve relative progression

    * between threads that would otherwise over-utilise a CPU. Its use

    * should be combined with detailed profiling and benchmarking to

    * ensure that it actually has the desired effect.

    * 该方法会放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间

    * 但放弃的时间不确定,可能刚刚放弃又获得CPU时间片

    *

    It is rarely appropriate to use this method. It may be useful

    * for debugging or testing purposes, where it may help to reproduce

    * bugs due to race conditions. It may also be useful when designing

    * concurrency control constructs such as the ones in the

    * {@link java.util.concurrent.locks} package.

    * 该方法的适合使用场景比较少,主要用于Debug,比如Lock包设计

    */

    public static native void yield();

    ■ interrupt 方法

    /**

    * Interrupts this thread.

    * 中断一个线程

    *

    Unless the current thread is interrupting itself, which is always permitted,

    * the {@link #checkAccess() checkAccess} method of this thread is invoked,

    * which may cause a {@link SecurityException} to be thrown.

    * 如果当前线程不是被自己中断,可能会抛出SecurityException异常

    *

    If this thread is blocked in an invocation of the {@link Object#wait() wait()},

    * {@link Object#wait(long) wait(long)}, or {@link Object#wait(long, int) wait(long, int)}

    * methods of the {@link Object} class, or of the {@link #join()}, {@link #join(long)}, {@link

    * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},methods of this class,

    * then its interrupt status will be cleared and it will receive an {@link InterruptedException}.

    * 若当前线程已被Object.wait()方法、Thread的join()或sleep方法阻塞,

    * 则调用该中断方法会抛出InterruptedException同时中断状态会被清除

    *

    If this thread is blocked in an I/O operation upon an {@link

    * java.nio.channels.InterruptibleChannel interruptible channel}

    * then the channel will be closed, the thread's interrupt status will be set,

    * and the thread will receive a {@link java.nio.channels.ClosedByInterruptException}.

    * 若当前线程在InterruptibleChannel上发生IO阻塞,该通道要被关闭并将线程状态设置为中断同时抛出

    * ClosedByInterruptException异常

    *

    If this thread is blocked in a {@link java.nio.channels.Selector} then the thread's

    * interrupt status will be set and it will return immediately from the selection operation,

    * possibly with a non-zero value, just as if the selector's {@link

    * java.nio.channels.Selector#wakeup wakeup} method were invoked.

    * 若该线程被选择器阻塞,将线程状态设置为中断同时从选取方法中立即返回

    * 该选取方法通常会返回一个非0值,当wakeup方法正好被调用时

    *

    If none of the previous conditions hold then this thread's interrupt status will be set.

    * 非上述情况都会将线程状态设置为中断

    *

    Interrupting a thread that is not alive need not have any effect.

    * 中断一个非活线程不会有啥影响

    * @throws SecurityException if the current thread cannot modify this thread

    * @revised 6.0

    * @spec JSR-51

    */

    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();

    }

    }

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

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

    展开全文
  • java多线程如何创建多个多线程java中如何启动一个新的线程有时候往往直到离开的时候,才能知道自己多喜欢一座城。很多事情只有经历之后才知道,同一件事情,和不同的人也会是另外一个世界时间就是这个样子,徜徉其中...

    java多线程如何创建多个多线程

    java中如何启动一个新的线程有时候往往直到离开的时候,才能知道自己多喜欢一座城。很多事情只有经历之后才知道,同一件事情,和不同的人也会是另外一个世界时间就是这个样子,徜徉其中尚觉得慢,一旦定睛回望,弹指之间。

    java多线程的情况下如何进行调试?

    小编用debug停下一个线程以后,发现另一个线程仍然继续工作,小编所要的另一她洗干净了头发,站在晌午的太阳下等你,头微微向左偏,湿发似春水畅流在左边闪闪发光。长发干了,柔软清凉,你还没来。后来,她洗完头依旧不用吹风筒呜呜呜地吹干,她剪了短发,变得清瘦,不再需要太阳来晒头发。她也不等你了。

    Java中关于如何实现多线程消息队列的实例少女站在樱花树下,漫天的樱瓣在风中飞舞,在那个月光的照耀里,少女的哀伤化作了容颜一角。

    java中的消息队列 消息队列是线程间通讯的手段: import java.util.* public class MsgQueue{ private Vector queue = null; public MsgQueue(){ queue = new Vector(); } public synchronized void send(Object o) { queue.addElement(o); } pu就算有一天小编的眼睛看不见了,小编还是会认出你,小编的心会看到你。

    Java如何实现多线程同步?

    现在主线程到了某个位置时创建并启动了3个子线程:t等一个不爱你的人,就像在夏天里等雪,在冬天里等花开。小编不等你,等自己死心。

    //解决方案-1 设置3把锁, 然后把锁们应用到所有线程中 (涉及到synchronized wait notify等, 嫌麻烦. 略) 解决方案-2 设置3个全局共享的信号标记(信号灯) + 3子线程分别占用标记1 2 3 + 主线程轮询/等待 (简洁明快 推荐) //解决方案-2 实现如下: 人们大多数只对有安全度的人发脾气,因为在那个安全度之内,你潜意识知道对方不会离开你。

    如何实现springMVC的多线程并发?

    实现springMVC的多线程并发: 小编不敢说自己一生都会喜欢你,但至少在能看见你的岁月里只想对你一个人好。

    ThreadLocal为解决多线程程序的并发问题提供了一种新的思路 与其在别人的生活里跑龙套,不去精彩的做自己。

    对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。

    java多线程都有几种方式实现??

    有三种: (1)继承Thread类,重写run函数 创建: class xx extends Thread{ public void run(){ Thread.sleep(1000)//线程休眠1000毫秒,sleep使线程进入Block状态,并释放资源 }} 开启线程: 对象.start()//启动线程。

    Java如何实现线程之间的互斥

    临界区(Critical Section):适合一个进程内的多线程访问公共区域或代码段时使用 Java如何实现线程之间的互斥 互斥量 (Mutex):适合不同进程内多线程访问公共区域或代码段时使用,与临界区相似。 事件(Event):通过线程间触发事件实现同步互小编一生气就想买东西,一买东西就得花钱,一花钱钱就少,钱一少小编就生气。。。

    以上就是四十五资源网为大家整理的java如何实现多线程 JAVA中如何利用多线程同时运行多个方法?内容,如果觉得本站更新的资源对您有帮助 不要忘记分享给您身边的朋友哦!

    展开全文
  • Java多线程整理(li)

    2021-03-04 00:48:23
    不同的是,当线程调用await方法后,必须所有线程都到达了,才能分别进入后面的执行过程。一旦有一个线程未到达,所有线程都会等待,有点像一部电影的名字《一个都不能少》。 /** 考虑一个应用场景: * 老师带领学生...

    目录:

    1.volatile变量

    2.Java并发编程学习

    3.CountDownLatch用法

    4.CyclicBarrier使用

    5.BlockingQueue使用

    6.任务执行器Executor

    7.CompletionService使用

    8.ConcurrentHashMap使用

    9.Lock使用

    892f91aa9b04c9da2fa346e36d033160.png

    一、 volatile变量

    1.volatile原理:volatile的原理实际上是告诉处理器,不要把变量缓存在寄存器或者相对于其他处理器不可见的地方,而是把变量放在主存,每次读写操作都在主存上进行操作。另外,被申明为volatile的变量也不会与其它内存中的变量进行重排序。

    2.volatile同步:volatile是同步的一个子集,只保证了变量的可见性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。相对于同步而言,volatile的优势:a.简易性,可以像使用其他变量一样使用volatile变量;b.volatile变量不会造成线程阻塞;c.如果读操作远远大于写操作,volatile 变量还可以提供优于锁的性能优势。

    3.正确使用volatile条件:对变量的写操作不依赖于当前值;该变量没有包含在具有其他变量的不变式中;

    /** 对于第一条原则:对变量的写操作不依赖于当前值;

    * 虽然i++只有一条语句,实际上这条语句是分三步执行的,读入i,i加1,写入i;

    * 若在第三步执行过程前,其他线程对i进行了改动,此时的结果将是错的。因此即使使用了volatile进行控制,并不能保证这个操作是线程安全的。*/

    private volatile int i=1;

    ...

    i++;/** 这类问题的解决方案有两种:

    * 1.一种是采用synchronized进行同步控制,这显然违背了volatile的初衷

    * 2.一种是采用CPU原语进行控制。在jdk1.5之后,java.util.concurrent.atomic包下的很多类就是采用这种方式进行控制,这样可以在保持性能的情况下,保证数据的线程安全。*/

    二、Java并发编程学习

    在java 并发编程实践中对线程安全的定义如下:当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替运行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么这个类就是线程安全的。完全由线程安全的类构成的程序不一定就是线程安全的。

    /** 如下代码所示:

    * 虽然Vector是一个线程安全的类,但是对于由Vector线程安全的方法组成上面的逻辑,显然不是线程安全的。

    * 当然,由线程不安全的类构成的程序,也不一定不是线程安全的。*/Vector v;if(!v.contains(o)){

    v.add(o);

    }/** 微妙的可见性:

    * 当变量的读入写出被不同的线程共享时,必须使用同步。若不使用同步将有可能发生与直觉大相径庭的错误。*/

    public classTestVisiable {static int x = 0, y = 0;static int b = 0, a = 0;public static void main(String[] args) throwsInterruptedException {

    Thread t1= new Thread(newRunnable() {

    @Overridepublic voidrun() {

    a= 1;

    x=b;

    }

    });

    Thread t2= new Thread(newRunnable() {

    @Overridepublic voidrun() {

    b= 1;

    y=a;

    }

    });

    t1.start();

    t2.start();

    t1.join();

    t2.join();

    System.out.println(x+ " " +y);

    }

    }/** 由于没有同步,程序运行的可能结果为(1,0) (0,1),(1,1),然而,还有可能为(0,0),不可思议吧。

    * 1.这是由于为了加快并发执行,JVM内部采用了重排序的机制导致。

    * 2.最低限的安全性在java中,java存储模型要求java变量的获取和存储都是原子操作,但是有两种类型的变量是比较特殊的,没有申明为volitale的long和double变量。

    * 原因在于:

    * 1.在java中long和double变量为64位,jvm允许将64位的读写操作划分为两个32位的操作。

    * 2.当多个线程同时读写非volatile的long或者double类型数据时,将有可能得到数据是一个数的高32位和另一个数的低32位组成的数。

    * 3.这样的结果显然是完全不对的。因此对应long或double类型的数据用于多线程共享时,必须申明加上volatile或者进行同步。*/

    三、CountDownLatch用法

    在jdk API中如下描述:一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。用给定的计数初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。

    CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。

    /** 下面给出一个详细的应用场景 百米赛跑:

    * 比赛共有10名运动员参与,10名运动在起跑线上统一等待号令员发起起跑的号令,通过终点时号令员统计该运动员的时间,当所有运动员都跑到终点时,报告每个人的成绩。

    * 这里实际上就可以采用CountDownLatch来解决起跑线上设置一个CountDownLatch,当号令员没有发起起跑号令时,所有运动员都在起跑线上等待。

    * 在终点设置一个CountDownLatch,起跑后号令员一直等待,当所有运动员都通过终点后,它会报告成绩。 用代码实现如下:*/

    importjava.util.HashMap;importjava.util.Map;importjava.util.concurrent.CountDownLatch;public classCommander {private final CountDownLatch startSignal; //起跑信号

    private final CountDownLatch endSignal; //终点信号

    privateLong startTime;private final Map scoreMap = new HashMap();public voidwaitStart() {try{

    startSignal.await();

    }catch(InterruptedException e) {

    e.printStackTrace();

    }

    }public synchronized voidstart() {

    startTime=System.currentTimeMillis();

    startSignal.countDown();try{

    endSignal.await();

    }catch(InterruptedException e) {

    e.printStackTrace();

    }

    }public void reach(intid) {

    endSignal.countDown();

    scoreMap.put(id, System.currentTimeMillis()- startTime);//统计时间

    }public Commander(intnum){

    startSignal= new CountDownLatch(1);

    endSignal= newCountDownLatch(num);

    }public static voidmain(String[] args) {int runnerNum = 10;

    Commander c= newCommander(runnerNum);for (int i = 0; i < runnerNum; i++) {new Thread(new Runner(i + 1, c)).start();

    }

    c.start();//发起号令

    for(Integer i : c.scoreMap.keySet()) {

    System.out.println(i+ "号运动员,耗时" +c.scoreMap.get(i));

    }

    }

    }class Runner implementsRunnable {

    Commander commander;intid;public Runner(intid, Commander commander){this.id =id;this.commander =commander;

    }

    @Overridepublic voidrun() {

    commander.waitStart();try{

    Thread.sleep((long) (Math.random() * 1000));

    }catch(InterruptedException e) {

    e.printStackTrace();

    }

    commander.reach(id);

    }

    }

    四、CyclicBarrier使用

    CyclicBarrier在jdk中描述:一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环的 barrier。

    实际上CyclicBarrier类似于CountDownLatch也是个计数器。不同的是,当线程调用await方法后,必须所有线程都到达了,才能分别进入后面的执行过程。一旦有一个线程未到达,所有线程都会等待,有点像一部电影的名字《一个都不能少》。

    /** 考虑一个应用场景:

    * 老师带领学生去春游,下午回来时,需要整队,然后统一坐车回去。

    * 在这里,老师和已经到达的学生,必须等待所有学生归队后才能回去。 用代码实现如下:*/

    importjava.util.concurrent.BrokenBarrierException;importjava.util.concurrent.CyclicBarrier;public classTeacher {private final CyclicBarrier barrier = new CyclicBarrier(20, newRunnable() {

    @Overridepublic voidrun() {

    System.out.println("所有学生已经归队");

    }

    });public void comeBack(inti) {

    System.out.println(i+ "已经归队。");try{

    barrier.await();

    }catch(InterruptedException e) {

    e.printStackTrace();

    }catch(BrokenBarrierException e) {

    e.printStackTrace();

    }

    }public static voidmain(String[] args) {

    Teacher t= newTeacher();for (int i = 0; i < 20; i++) {new Thread(new Student(i + 1, t)).start();

    }

    }

    }class Student implementsRunnable {private final intid;private finalTeacher teacher;public Student(intid, Teacher teacher){this.id =id;this.teacher =teacher;

    }

    @Overridepublic voidrun() {try{

    Thread.sleep((long) (Math.random() * 1000));

    teacher.comeBack(id);

    System.out.println(id+ "上车");

    }catch(InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    五、BlockingQueue 学习

    从名字上看,BlockingQueue是阻塞队列的意思。这个队列主要提供下面的功能:

    1.阻塞队列提供了可阻塞的take和put方法,另外可定时的poll和offer实际原理也是一样的。

    2.如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒,同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间时才会被唤醒继续操作。

    阻塞队列有一般又为两类:无限阻塞队列和有限阻塞队列。有限阻塞队列中,当队列满时,调用put方法将会阻塞;而无限阻塞队列中,put方法是不会阻塞的。很显然这个队列的一种最常用的场景就是:生产者-消费者 模式。

    /*它有以下具体实现类:

    * ArrayBlockingQueue:采用数组作为存储队列的阻塞队列,这个队列采用FIFO的方式管理数据

    * LinkedBlockingQueue:采用链式结构作为存储队列,同样它也采用FIFO的方式管理数据

    * PriorityBlockingQueue:采用基于优先级堆的极大优先级队列作为存储队列。

    * SynchronousQueue:特殊的BlockingQueue,其中每个 put 必须等待一个 take,反之亦然。

    * DelayQueue:这是一个无限阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部 是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部。

    *

    * 通过查看源代码可以看到这个类内部是采用PriorityQueue作为存储队列

    * DelayQueue的一些常用的场景

    * a) 关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。

    * b) 缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。

    * c) 任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求。

    **/

    六、任务执行器Executor

    记得在大学的时候,有一次写程序时,需要创建很多的线程来处理各种socket请求,于是写了一个线程类,每出现一个socket请求,就创建一个线程。后来,老师指出,每次创建线程的开销比较大,可以将线程与具体的业务逻辑分离开来,然后用一个队列,保存一定量的线程,每次需要的时候就去取,不用的时候就还回去,这样可以循环使用,避免重复创建线程的开销,于是乎,对代码进行重构,写了大段的代码,发现以后需要用到多线程的地方都可要用它,后来在网上找资料才知道,那叫线程池。

    jdk1.5的升级,给我们带来一个很特殊的包java.util.concurrent,翻阅API,可以看到jdk中已经封装了线程池,简单两行便可实现。这个包中提供了Executor的接口,接口定义如下:

    void execute(Runnable command);

    传入一个runnable接口的实现,它便自动为我们创建线程和执行,任务的提交者再也不用为了并发执行,自己写一大段代码来创建并管理各种线程。扩展了Executor的有ExecutorServicelinkext7.gif, ScheduledExecutorServicelinkext7.gif,AbstractExecutorServicelinkext7.gif, ScheduledThreadPoolExecutorlinkext7.gif, ThreadPoolExecutor.linkext7.gif由于Runnable接口中只提供了一个不带返回值run方法,因此当任务需要返回值时,Executor就不能满足需求了,于是出现了ExecutorService,这个接口继承了Executor,对提交任务的接口进行了扩展,引入了Callable接口,该接口定义如下:

    public interface Callable{

    V call()throwsException;

    }

    同时接口将任务执行过程进行管理,分为三个状态,提交,shutdown,terminate。在AbstractExecutorService中可以看到submit(Callable c)的实现,实际上它会先创建一个Future对象,然后再调用execute(Runnable command)方法,执行任务。可以很明显的知道Future肯定是继承了Runnable接口。通过Future接口,我们可以获取由call方法调用的返回值。Executor接口的两个具体实现是ThreadPoolExecutor和ScheduledThreadPoolExecutor,通过名字可以看出,ThreadPoolExecutor是通过采用线程池来执行每个提交的任务,ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,主要用于满足延迟后运行任务,或者定期执行任务的需求。虽然,我们可以通过直接调用ThreadPoolExecutor和ScheduledThreadPoolExecutor的构造函数生成Executor,但是很多情况下,没有这个必要,因为jdk为我们作了简化工作,通过Executors这个工厂类,可以只需传入一些简单的参数,便可以得到我们需要的Executor对象。

    七、CompletionService学习

    若在采用Executor执行任务时,可用通过采用Future来获取单个任务执行的结果,在Future中提供了一个get方法,该方法在任务执行返回之前,将会阻塞。当向Executor提交批处理任务时,并且希望在它们完成后获得结果,如果用FutureTask,你可以循环获取task,并用future.get()去获取结果,若没有完成则阻塞,这对于对任务结果需要分别对待的时候是可行的。但是若所有task产生的结果都可以被同等看待,这时候采用前面这样的方式显然是不可行了,因为若当前的task没有完成,而后面的其它task已经完成,你也得等待,这个实效性不高。显然很多时候,统一组task产生的结果都应该是没有区别的,也就是满足上述第二种情况。这个时候咋办呢?jdk为我们提供了一个很好的接口CompletionService,这个接口的具体实现类是ExecutorCompletionService。该类中定义下面三个属性:

    private finalExecutor executor;private finalAbstractExecutorService aes;private final BlockingQueue> completionQueue;

    executor由构造函数传入,aes只是用于生成Future对象。特别要注意是completionQueue。它维护了一个保存Future对象的BlockingQueue。当这个Future对象状态是结束的状态的时候,也就是task执行完成之后,会将它加入到这个Queue中。到底是在哪里将完成的Future加入到队列里面的呢?又是怎么知道task是什么时候结束的呢?在ExecutorCompletionService中定义了一个QueueingFuture类,该类的实现:

    private class QueueingFuture extends FutureTask{

    QueueingFuture(RunnableFuturetask) {super(task, null);this.task =task;

    }protected voiddone() { completionQueue.add(task); }private final Futuretask;

    }

    可以看到在done方法中,它会把当前的task加入到阻塞队列中。追踪done方法可以看到,该方法定义在FutureTask中,默认实现为空,从注释可以看出,当Future的状态转为isDone的时候,就会调用该方法。调用端在调用CompletionService的take方法时,实际上调用的是BlockingQueue的take方法,由前面的学习中,我们知道,当队列中有内容时,该队列会立即返回队列中的对象,当队列为空时,调用线程将会阻塞。而只要有任务完成,调用线程就会跳出阻塞,获得结果。

    八、ConcurrentHashMap学习

    java中提供对HashMap是我们使用得比较多的一个类,单该类是非线程安全的,若处于多线程环境中,则需要通过synchronized关键字进行同步(通过查看Collections.synchronizedMap方法,可以知道,实际上该方法对实现也是通过synchronized关键字进行同步控制),虽然它能保证线程安全,但是,由于需要对整个map进行加锁,这样做的并发性能往往不是很理想,尤其是map中数据量比较大的时候。jdk1.5之后,java.util.concurrent包中提供了ConcurrentHashMap,一方面,该类自己保证了线程安全性,另一方面,该类也提供了一些复合操作的原子性接口。

    ConcurrentHashMap与HashMap一样,同样是哈希表,但是采用不同对锁机制--分离锁,即采用不同的锁来同步不同的数据块,以减少对锁对竞争。   在ConcurrentHashMap内部采用了一个包含16个锁对象对数组,每个锁负责同步hash Bucket的1/16,bucket中的每个对象通过它的hashCode计算得到它所在锁对象。假设hash算法的实现能够提供合理的扩展性,并且关键字能够以统一的方式访问,这会将对于锁的请求减少到原来的1/16.这种基于分离锁设计的技术实现能够使得ConcurrentHashMap支持16个并发的请求。

    九、Lock使用

    在java5.0以前都是采用synchronized关键字进行同步控制,所有对象都自动含有单一的锁,JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会递增。只有首先获得锁的任务(线程)才能继续获取该对象上的多个锁。每当任务离开一个synchronized方法,计数递减,当计数为0的时候,锁被完全释放,此时别的任务就可以使用此资源。由于这些锁是由JVM来控制,因此也叫隐式锁。

    在jdk5.0以后,提供了显示锁,即Lock,可以说是对隐式锁的功能的扩展,主要有两个。一个是ReentrantLock,另一个是ReentrantReadWriteLock,前者是普通重入锁,后者是可重入的读写锁。这些锁提供了两种锁竞争机制:公平竞争和非公平竞争,公平竞争的实现实际上是采用一个队列保存等待的线程,当当前线程释放锁之后,取出队头的线程唤醒,使之可以获取锁。java中Lock接口的定义:

    public interfaceLock {voidlock();void lockInterruptibly() throwsInterruptedException;booleantryLock();boolean tryLock(long time, TimeUnit unit) throwsInterruptedException;voidunlock();

    Condition newCondition();

    }

    Lock与synchronized的区别:

    1.synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁。而Lock的释放必须由程序自己保证,常用的写法是把是把释放锁的代码写到try{}finally中,在finally块中释放锁。

    2.如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断,如果使用Lock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情。

    3.synchronize实际上是ReentrantLock和Condition的组合的简化版

    4.相对于synchonized独占锁,ReentrantReadWriteLock通过分离读锁和写锁,提供可共享的读锁提高读并发性能。

    展开全文
  • Java多线程&高并发

    2021-03-09 09:15:00
    一、线程安全性定义:当线程访问某个类时,不管运行时环境采用何种调度方式,或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全...

    一、线程安全性

    定义:当多个线程访问某个类时,不管运行时环境采用何种调度方式,或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

    1. 原子性:提供了互斥访问,同一时刻只能有一个线程来对它进行访问。

    Atomic包:

    AtomicXXX:CAS、Unsafe.compareAndSwapInt

    AtomicLong、LongAdder

    AtomicReference、AtomicReferenceFieldUpdater

    AtomicStampReference:CAS的ABA问题

    原子性 - synchronized(同步锁)

    修饰代码块:大括号括起来的代码,作用于调用的对象

    修饰方法:整个方法,作用于调用的对象

    修饰静态方法:整个静态方法,作用于所有对象

    修饰类:括号括起来的部分,作用于所有类

    原子性 - 对比

    synchronized:不可中断锁,适合竞争不激烈,可读性好

    Lock:可中断锁,多样化同步,竞争激烈时能维持常态

    Atomic:竞争激烈时能维持常态,比Lock性能好;只能同步一个值

    2. 可见性:一个线程对主内存的修改可以及时的被其他线程观察到。

    导致共享变量在线程见不可见的原因:

    线程交叉执行

    冲排序结合线程交叉执行

    共享变量更新后的值没有在工作内存与主内存之间急事更新

    synchronized、volatile

    JMM关于synchronized的两条规定:

    线程解锁前,必须把共享变量的最新制刷新到主内存

    线程加锁前,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁与解锁是同一把锁)

    volatile - 通过加入内存屏障和禁止重排序优化来实现

    对volatile变量写操作时,会在写操作后加入一条store屏障指令,将本地内存中的共享变量值刷新到主内存

    对volatile变量读操作时,会在读操作前加入一条load屏障指令,从主内存中读取共享变量

    volatile变量在每次被线程访问时,都强迫从主内存中读取该变量的值,而当变量的值发生变化时,又会强迫线程将该变量最新的值强制刷新到主内存,这样一来,任何时候不同的线程总能看到该变量的最新值

    3. 有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序。

    Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。volatile、synchronized、Lock。

    【volatile变量规则】:对一个变量的写操作先行发生于后面对这个变量的读操作。(如果一个线程进行写操作,一个线程进行读操作,那么写操作会先行于读操作。)

    【传递规则】:如果操作A先行于操作B,而操作B又先行于操作C,那么操作A就先行于操作C。

    【线程启动规则】:Thread对象的start方法先行发生于此线程的每一个动作。

    【线程中断规则】:对线程interrupt方法的调用先行发生于被中断线程的代码检测到中断事件的发生。

    【线程终结规则】:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()方法的返回值手段检测到线程已经终止执行。

    【对象终结规则】:一个对象的初始化完成先行发生于他的finalize()方法的开始。

    二、发布对象

    发布对象:使一个对象能够被当前范围之外的代码所用。

    对象溢出:一种错误的发布。当一个对象还没有构造完成时,就使它被其他线程所见。

    三、安全发布对象

    在静态初始化函数中初始化一个对象

    将对象的引用保存到volatile类型域或者AtomicReference对象中

    将对象的引用保存到某个正确构造对象的final类型域中

    将对象的引用保存到一个由锁保护的域中

    /**

    * 懒汉模式

    * 双重同步锁单例模式

    * @author Guo

    *

    */

    public class SingletonExample1 {

    private SingletonExample1(){

    }

    // volatile禁止指令重排

    private volatile static SingletonExample1 instance = null;

    public static SingletonExample1 getInstance(){

    if(instance == null){

    synchronized(SingletonExample1.class){

    if(instance == null){

    instance = new SingletonExample1();

    }

    }

    }

    return instance;

    }

    }

    四、避免并发两种方式

    不可变对象

    线程封闭

    线程封闭: 把对象封装到一个线程里,只有这一个线程可以看到这个对象,即使这个对象不是线程安全也不会出现任何线程安全问题,因为只在一个线程里

    堆栈封闭:局部变量,无并发问题。栈封闭是我们编程当中遇到的最多的线程封闭。什么是栈封闭呢?简单的说就是局部变量。多个线程访问一个方法,此方法中的局部变量都会被拷贝一分儿到线程栈中。所以局部变量是不被多个线程所共享的,也就不会出现并发问题。所以能用局部变量就别用全局的变量,全局变量容易引起并发问题。

    ThreadLocal线程封闭:比较推荐的线程封闭方式。

    【ThreadLocal结合filter完成数据保存到ThreadLocal里,线程隔离。】通过filter获取到数据,放入ThreadLocal, 当前线程处理完之后interceptor将当前线程中的信息移除。使用ThreadLocal是实现线程封闭的最好方法。ThreadLocal内部维护了一个Map,Map的key是每个线程的名称,而Map的值就是我们要封闭的对象。每个线程中的对象都对应着Map中一个值,也就是ThreadLocal利用Map实现了对象的线程封闭

    五、线程不安全类与写法

    【线程不安全】:如果一个类类对象同时可以被多个线程访问,如果没有做同步或者特殊处理就会出现异常或者逻辑处理错误。

    【1. 字符串拼接】:

    StringBuilder(线程不安全)、

    StringBuffer(线程安全)

    【2. 日期转换】:

    SimpleDateFormat(线程不安全,最好使用局部变量[堆栈封闭]保证线程安全)

    JodaTime推荐使用(线程安全)

    【3. ArrayList、HashSet、HashMap等Collections】:

    ArrayList(线程不安全)

    HashSet(线程不安全)

    HashMap(线程不安全)

    【**同步容器**synchronized修饰】

    Vector、Stack、HashTable

    Collections.synchronizedXXX(List、Set、Map)

    【**并发容器** J.U.C】

    ArrayList -> CopyOnWriteArrayList:(读时不加锁,写时加锁,避免复制多个副本出来将数据搞乱)写操作时复制,当有新元素添加到CopyOnWriteArrayList中时,先从原有的数组中拷贝一份出来,在新的数组上进行写操作,写完之后再将原来的数组指向新的数组。

    7437cecf4c6719fd2a95139e02bf8b28.png

    HashSet、TreeSet -> CopyOnWriteArraySet、ConcurrentSkipListSet:

    HashMap、TreeMap -> ConcurrentHashMap、ConcurrentSkipListMap:

    相比ConcurrentHashMap,ConcurrentSkipListMap具有如下优势:

    ConcurrentSkipListMap的存取速度是ConcurrentSkipListMap的4倍左右

    ConcurrentSkipListMap的key是有序的

    ConcurrentSkipListMap支持更高的并发(它的存取时间和线程数几乎没有关系,更高并发的场景下越能体现出优势)

    六、安全共享对象策略 - 总结

    线程限制:一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改

    共享只读:一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问,但是任何线程都不能修改它

    线程安全对象:一个线程安全的对象或者容器,在内部通过同步机制来保证线程安全,所以其他线程无需额外的同步就可以通过公共接口随意访问它

    被守护对象:被守护对象只能通过获取特定锁来访问

    七、J.U.C 之 AQS

    7.1、 AQS

    AQS:AbstractQueneSynchronizer

    使用Node实现FIFO队列,可以用于构建锁或者其他同步装置的基础框架

    利用int类型表示状态

    使用方法是继承

    子类通过继承并通过实现它的方法管理其状态{ acquire和release }的方法操纵状态

    可以同时实现排它锁和共享锁模式(独占、共享)

    7.2、 AQS的同步组件如下:

    7.2.1、CountDownLatch:闭锁,通过计数来保证线程是否一直阻塞.

    CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。构造器中的计数值(count)实际上就是闭锁需要等待的线程数量。这个值只能被设置一次,而且CountDownLatch没有提供任何机制去重新设置这个计数值。

    与CountDownLatch的第一次交互是主线程等待其他线程。主线程必须在启动其他线程后立即调用CountDownLatch.await()方法。这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。

    其他N 个线程必须引用闭锁对象,因为他们需要通知CountDownLatch对象,他们已经完成了各自的任务。这种通知机制是通过 CountDownLatch.countDown()方法来完成的;每调用一次这个方法,在构造函数中初始化的count值就减1。所以当N个线程都调 用了这个方法,count的值等于0,然后主线程就能通过await()方法,恢复执行自己的任务。

    解释一下CountDownLatch概念?

    `CountDownLatch`和 `CyclicBarrier`的不同之处?

    给出一些CountDownLatch使用的例子?

    CountDownLatch类中主要的方法?

    d4565c4d8639454899391b3f5df03aff.png

    public class CountDownLatchExample1 {

    // 线程数

    private final static int threadCount = 200;

    public static void main(String[] args) throws InterruptedException{

    // 使用线程池进行调度

    ExecutorService exec = Executors.newCachedThreadPool();

    final CountDownLatch countDownLatch = new CountDownLatch(threadCount);

    for (int i = 0; i < threadCount; i++) {

    final int threadNum = i;

    exec.execute(() -> {

    try {

    test(threadNum);

    } catch (Exception e) {

    System.out.println("exception:" + e);

    }finally{

    countDownLatch.countDown(); // 计数器减一

    }

    });

    }

    countDownLatch.await(10, TimeUnit.MILLISECONDS);

    System.out.println("===finished===");

    exec.shutdown();

    }

    private static void test(int threadNum) throws InterruptedException{

    Thread.sleep(100);

    System.out.println("threadNum:" + threadNum);

    }

    }

    7.2.2、Semaphore(信号量):可以控制同一时间并发线程的数目

    主要函数:acquire、release、tryAcquire

    public class SemaphoreExample1 {

    // 线程数

    private final static int threadCount = 20;

    public static void main(String[] args) throws InterruptedException{

    // 使用线程池进行调度

    ExecutorService exec = Executors.newCachedThreadPool();

    //并发控制(允许并发数20)

    final Semaphore semaphore = new Semaphore(3);

    final CountDownLatch countDownLatch = new CountDownLatch(threadCount);

    for (int i = 0; i < threadCount; i++) {

    final int threadNum = i;

    exec.execute(() -> {

    try {

    if(semaphore.tryAcquire(5, TimeUnit.SECONDS)){

    test(threadNum);

    semaphore.release();

    }

    /** 多个许可:在代码中一共有10个许可,每次执行semaphore.acquire(5);

    * 代码时耗费掉5个,所以20/5=4,

    * 说明同一时间只有4个线程允许执行acquire()和release()之间的代码。

    * */

    // semaphore.acquire(3); // 获取许可

    // test(threadNum);

    // semaphore.release(3); // 释放许可

    } catch (Exception e) {

    System.out.println("exception:" + e);

    }finally{

    countDownLatch.countDown(); // 计数器减一

    }

    });

    }

    // countDownLatch.await(100, TimeUnit.MILLISECONDS);

    System.out.println("===finished===");

    exec.shutdown();

    }

    private static void test(int threadNum) throws InterruptedException{

    System.out.println("threadNum:" + threadNum);

    Thread.sleep(1000);

    }

    }

    7.2.3、CyclicBarrier:可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行

    应用场景:需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用CyclicBarrier。

    简单理解【`人满发车`】:

    长途汽车站提供长途客运服务。

    当等待坐车的乘客到达20人时,汽车站就会发出一辆长途汽车,让这20个乘客上车走人。

    等到下次等待的乘客又到达20人是,汽车站就会又发出一辆长途汽车。

    public class CyclicBarrierExample1 {

    // 线程数

    private final static int threadCount = 10;

    // 屏障的线程数目 5

    private static CyclicBarrier barrier = new CyclicBarrier(5, () -> {

    System.out.println("===continue===");

    });

    public static void main(String[] args) throws InterruptedException {

    ExecutorService executorService = Executors.newCachedThreadPool();

    for (int i = 0; i < threadCount; i++) {

    final int threadNum = i;

    Thread.sleep(500);

    executorService.execute(() -> {

    try {

    race(threadNum);

    } catch (Exception e) {

    e.printStackTrace();

    }

    });

    }

    }

    private static void race(int threadNum) throws Exception {

    Thread.sleep(1000);

    System.out.println("===" + threadNum + " is ready.");

    try{

    barrier.await(2000, TimeUnit.MILLISECONDS);

    }catch(Exception e){

    System.out.println("e:"+e);

    }

    System.out.println("===" + threadNum + " continue");

    }

    }

    7.2.4、ReentrantLock

    1. api:

    - lock()

    - unlock()

    - tryLock()

    private static Lock lock = new ReentrantLock();

    private static void test(int threadNum){

    lock.lock();

    try{

    count++;

    }finally{

    lock.unlock();

    }

    }

    2. ReentrantLock和synchronized的区别

    - 1. `可重入性`

    - 2. `锁的实现`:synchronized是jvm实现,ReentrantLock是jdk实现

    - 3. `性能区别`

    - 4. `功能方面的区别`

    3. ReentrantLock独有的功能

    - 1. 可指定是公平锁还是非公平锁,synchronized只能是非公平锁(公平锁:先等待的线程先获得锁)

    - 2. 提供了一个Condition类,可以分组唤醒需要唤醒的线程

    - 3. 提供能够中断等待锁的线程的机制,lock.lockInterruptibly()

    4. ReentrantReadWriteLock

    5. StampedLock

    6. 锁的使用

    - 当只有少量竞争者线程的时候,`synchronized`是一个很好的通用的锁的实现(synchronized不会引发死锁,jvm会自动解锁)

    - 竞争者线程不少,但是线程增长的趋势是可以预估的,这时候使用`ReentrantLock`是一个很好的通用的锁的实现

    7.2.5、Condition

    public class LockExample3 {

    public static void main(String[] args){

    ReentrantLock reentrantLock = new ReentrantLock();

    Condition condition = reentrantLock.newCondition();

    int u=1;

    new Thread(() -> {

    try{

    reentrantLock.lock();

    System.out.println("wait signal"); // 1

    condition.await();

    }catch(InterruptedException e){

    e.printStackTrace();

    }

    System.out.println("get signal");

    reentrantLock.unlock();

    }).start();

    new Thread(() -> {

    reentrantLock.lock();

    System.out.println("get lock");

    try{

    Thread.sleep(3000);

    }catch(InterruptedException e){

    e.printStackTrace();

    }

    condition.signalAll();

    System.out.println("send signal");

    reentrantLock.unlock();

    }).start();

    }

    }

    7.2.6、FutureTask

    创建线程两种方式继承Thread,实现Runnable接口,这两种方式,在任务执行完毕之后获取不到执行结果

    FutureTask、Callable可以获取到执行结果

    1. Callable和Runnable对比

    2. Future接口

    3. FutureTask

    ```

    public static void main(String[] args) throws InterruptedException, ExecutionException {

    FutureTask futureTask = new FutureTask(new Callable() {

    @Override

    public String call() throws Exception {

    System.out.println("do something in callable...");

    Thread.sleep(3000);

    return "Done";

    }

    });

    new Thread(futureTask).start();

    System.out.println("do something in main...");

    Thread.sleep(1000);

    String result = futureTask.get();

    System.out.println("result:"+result);

    }

    }

    7.2.7、Fork/Join框架:将大模块切分成多个小模块进行计算

    八、线程池

    初始化好线程池实例之后,将任务丢进去等待调度执行。

    8.1、Thread弊端

    每次new Thread都要新建对象,性能差

    线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多的系统资源导致死机或者OOM

    缺少更多功能,如更多执行,定期执行,线程中断

    8.2、线程池的好处

    可以重用存在的线程,减少对象的创建、消亡的开销,性能佳

    可以有效的控制最大并发数,提供系统资源利用率,同时可以避免过多的资源竞争,避免阻塞

    提供定时执行、定期执行、单线程、并发数控制等功能

    【ThreadPoolExecutor的初始化参数】

    corePoolSize:核心线程数量

    maximumPoolSize:县城最大线程数

    workQueue:阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响

    keepAliveTime:线程没有任务执行时,最多保持多久时间终止

    unit:keepAliveTime的时间单位

    hreadFactory:线程工厂,用来创建线程

    rejectHandler:当拒绝处理任务时的策略

    线程池-ThreadPoolExecutor状态

    72db04e4f60dd2b033ad26e1a88b0832.png

    线程池-ThreadPoolExecutor方法

    1. execute():提交任务,交给线程池执行

    2. submit():提交任务能够返回执行结果execute + Future

    3. shutdown():关闭线程池,等待任务都执行完

    4. shutdownNow():关闭线程池,不等待任务执行完

    5. getTaskCount():线程池已执行和未执行的任务总数

    6. getCompletedTaskCount():已完成的任务总数

    7. getPoolSize():线程池当前的线程数量

    8. getActiveCount:当前线程池中正在执行任务的线程数量

    8.3、线程池 - Executors框架(创建线程池)

    Executors.newCachedThreadPool:创建一个可缓存的线程池,如果线程池长度超过了处理的需要可以灵活回收空闲线程,如果没有可以回收的,那么就新建线程

    public static void main(String[] args) {

    ExecutorService executorService = Executors.newCachedThreadPool();

    // 往线程池中放任务

    for (int i = 0; i < 10; i++) {

    final int index = i; // 任务的序号

    executorService.execute(() -> {

    System.out.println("===task:"+index);

    });

    }

    executorService.shutdown(); // 关闭线程池

    }

    Executors.newFixedThreadPool:创建的是一个定长的线程池,可以控制线程的最大并发数,超出的线程会在队列中等待

    Executors.newScheduledThreadPool:创建的也是定长线程池,支持定时以及周期性的任务执行

    public static void main(String[] args) {

    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);

    // 往线程池中放任务

    executorService.scheduleAtFixedRate(() -> {

    log.info("===sechedule run");

    }, 1, 3, TimeUnit.SECONDS); // 延迟一秒,每隔三秒执行任务

    executorService.schedule(() -> {

    log.info("===sechedule run");

    }, 3, TimeUnit.SECONDS);

    executorService.shutdown(); // 关闭线程池

    }

    Executors.newSingleThreadExecutor:创建的是一个单线程化的线程池,会用唯一的一个工作线程来执行任务,保证所有任务按照指令顺序执行(指令顺序可以指定它是按照先入先出,优先级执行)

    newSingleThreadExecutor打印结果是按照顺序输出

    aafeff4e7c87e8e207e9b2f1b83aca84.png

    8.4、线程池 - 合理配置

    1. CPU密集型任务,就需要尽量压榨CPU,参考可以设置为NCPU+1

    2. IO密集型任务,参考可以设置为2*NCPU

    > NCPU = CPU的数量

    > UCPU = 期望对CPU的使用率 0 ≤ UCPU ≤ 1

    > W/C = 等待时间与计算时间的比率

    > 如果希望处理器达到理想的使用率,那么线程池的最优大小为:

    > 线程池大小=NCPU *UCPU(1+W/C)

    展开全文
  • 函数调用约定常见的函数调用约定[5]:cdecl,stdcall,fastcall,thiscall,naked callMFC调用约定(VS6:Project Settings->C/C++ Calling convention:)1, __cdecl(C调用约定.The C default calling convention)C/C++ ...
  • java适合多线程编程,python适合多进程编程。因为python的多线程底层逻辑有同步锁,造成并发效果不佳。python的多进程可以利用多核CPU的计算优势进行并发,所以任务侧重CPU的计算就用多进程,任务侧重大量io的读取和...
  • Socket编程一、网络基础知识两台计算机要通过网络进行通信,必须具备:a、唯一的标识(IP地址);b、需要共同的语言(协议);...是以TCP和IP为基础的不同层次上个协议的集合。也称为:TCP/IP协议...
  • 1 先定义一个要调用业务的地方 /** * 线程任务类 */ class MyCallable implements Callable<Map<String, String>> { private String id; public MyCallable(String id) { this.id = id; ...
  • 展开全部根据不同的情2113况可能存在5261的情况有:1、如4102果该方法不涉及写1653公共的资源比如一个回静态的变量或者写文件,修改答某个数据库的值的时候没有影响。2、比如你这个类里的方法只是对输入的参数做一个...
  • 多线程(单例设计模式)(掌握)* 单例设计模式:保证类在内存中只有一个对象。* 如何保证类在内存中只有一个对象呢?* (1)控制类的创建,不让其他类来创建本类的对象。private* (2)在本类中定义一个本类的对象。...
  • [TOC]前言假设执行一条脚本...这就用到多线程了,理论上开2个线程时间节省一半,开5个线程,时间就缩短五倍了。## 项目结构1.项目结构跟之前的设计是一样的:* case test开头的.py用例脚本* common 放公共模块,如H...
  • Java多线程整理

    2021-03-01 06:23:38
    一、线程池过于频繁的创建/销毁线程浪费性能,线程并发数量过多,JVM调度是抢占式的,线程上线文切换抢占系统资源导致阻塞。1.线程池线程数:一般CPU密集型:CPU+1IO密集型:[(线程等待时间+线程CPU时间)/线程CPU...
  • Linux C多线程入门

    2021-05-14 00:48:10
    介绍:什么是线程线程的优点是什么线程在Unix系统下,通常被称为轻量级的...但同一进程中的线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage...
  • 在单线程操作List,Dictionary等集合的时候一点问题都没有,但是在多线程情况下,问题就出来了,经常会报错。我们可以看看源码,List的Add方法是线程不安全的,List的源码中的Add方法,使用了每次当当前的元素达到...
  • Ajax没有调用回调函数

    2021-08-07 07:51:20
    Ajax没有调用回调函数 ...当用户下发给GE的图中带有summary、checkpoint算子时,GE会调用该回调函数。Status RegisterCallBackFunc(const std::string &key, const pCallBackFunc &callback)回调函数类注册...
  • 【C++】类内使用多线程

    千次阅读 2020-12-25 13:55:03
    【C++】类内使用多线程 std::thread std::thread 是 C++ 11 引入的新特性,其使用也非常简单。由资源抢占所引发的加锁问题,使用 mutex 互斥量对公共变量施加保护固然可以有效地保障线程安全,但是这种方式的代价也...
  • 多线程面试题整理

    2021-01-22 10:52:55
    1.多线程有什么用? 1)发挥多核CPU 的优势 随着工业的进步,现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的 ,4 核、8 核甚至 16 核的也都不少见,如果是单线程的程序,那么在双核 CPU 上 就浪费了...
  • 但是我们使用多线程而不是多处理,因为线程共享一个公共内存区域。 它们不分配单独的内存区域,因此节省了内存,并且线程之间的上下文切换花费的时间少于进程。Java多线程主要用于游戏,动画等Java多线程的优点1)它...
  • 10.4 在多线程环境中使用传统 iostream本节介绍如何将 libC 和 libiostream 库的 iostream 类用于多线程环境的输入输出 (input-output, I/O)。另外,还提供了如何通过从 iostream 类派生来扩展库的功能的示例。但本...
  • 不如多个人做不同的事情 并发:一个CPU做多个任务,秒杀多个用户需要进行操作,一个管理来负责多个用户任务 为什么需要多线程 背景:以单核CPU为例,只使用单个线程先后完成多个任务(调用多个方 法), 肯定比用多...
  • 我们做量化交易时可能需要多品种多策略同时运行,就需要用到多线程或异步协程来实现,本文便给出一个实现多线程和异步协程的策略框架。线程和协程的概念可以在网上查阅相关资料,本文就不详述了。本文代码基于天勤...
  • 原文:...线程是比进程更小的执行单位,一个进程在其执行过程中,可以产生线程,所以又称线程为“轻型进程”。虽然说可以并发运行线程,但在任何时刻cp...
  • Java多线程

    2021-10-09 15:31:33
    Java多线程并行和并发线程基础概念实现多线程的两种方式锁的概念和使用生产消费者模型volatile变量线程池的概念和使用 并行和并发 线程基础概念 实现多线程的两种方式 锁的概念和使用 生产消费者模型 volatile变量 ...
  • android多线程机制

    2021-05-27 06:50:16
    一.Android多线程由来当我们启动一个App的时候,Android系统会启动一个LinuxProcess,该Process包含一个Thread,称为UI Thread或Main Thread。通常一个应用的所有组件都运行在这一个Process中,当然,你可以通过修改...
  • 9.2 Linux线程编程9.2.1 线程基本编程这里...创建线程实际上就是确定调用线程函数的入口点,这里通常使用的函数是pthread_create()。在线程创建以后,就开始运行相关的线程函数,在该函数运行完之后,该线程也就退...
  • python 进程同步运行函数

    千次阅读 2021-11-12 16:02:06
    场景:一个进程控制吃饭函数,另一个进程控制睡觉函数,同步进行。 结论,运行时间:不用进程 > 函数形式进程 == 类形式进程 目录 1.不用进程 2.函数形式进程multiprocessing.Process() 3.类形式进程...
  • Future接口封装了取消,获取线程结果,以及状态判断是否取消,是否完成这几个方法 demo: 使用线程池提交Callable接口任务,返回Future接口,添加进list,最后遍历FutureList且内部使用while轮询,并发获取结果 (1)、...
  • 申明:本学习笔记是在该教程的基础上结合自己的学习情况进行的总结,不是原创,想要看原版的请看C语言中文网的多线程编程(C语言+Linux),该网站有很多好的编程学习教程,尤其是关于C语言的。 前面章节中,我们...
  • 多线程 1.线程 进程 多线程 多线程:多条执行路径,主线程与子线程并行交替执行(普通方法只有主线程一条路径)。 程序:指令和数据的有序集合,本身没有任何运行的含义,是一个静态的概念。 进程:在操作系统中运行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 80,962
精华内容 32,384
关键字:

多线程调用公共函数