关于java:Java的多线程和高并发详解

90次阅读

共计 17162 个字符,预计需要花费 43 分钟才能阅读完成。

前言

多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具备多线程能力的计算机因有硬件反对而可能在同一时间执行多于一个线程,进而晋升整体解决性能。具备这种能力的零碎包含对称多处理机、多外围处理器以及芯片级多解决或同时多线程处理器。

原理并不简单,但实现起来可没那么简略,在网上看到两张图,我感觉形容多线程很是活泼,给大伙看看

现实多线程

事实多线程

是不是感觉很形象?

篇幅所限,这篇文章咱们就先讲讲过程与线程、并发与并行的外围原理以及线程的创立,如果想更深刻的理解多线程,我这里也整顿了一些多线程的学习材料与面试材料,如下:

关注公众号:北游学 Java,回复“630”即可支付

一、过程与线程

过程:是代码在数据汇合上的一次运行流动,是零碎进行资源分配和调度的根本单位。

线程:是过程的一个执行门路,一个过程中至多有一个线程,过程中的多个线程共享过程的 资源。

尽管零碎是把资源分给过程,然而 CPU 很非凡,是被调配到线程的,所以线程是 CPU 调配的根本单位。

二者关系:

一个过程中有多个线程,多个线程共享过程的堆和办法区资源,然而每个线程有本人的程序计数器和栈区域。

 程序计数器:是一块内存区域,用来记录线程以后要执行的指令地址。

:用于存储该线程的局部变量,这些局部变量是该线程公有的,除此之外还用来寄存线程的调用栈祯。

:是一个过程中最大的一块内存,堆是被过程中的所有线程共享的。

办法区:则用来寄存 NM 加载的类、常量及动态变量等信息,也是线程共享的。

二者区别:

过程:有独立的地址空间,一个过程解体后,在保护模式下不会对其它过程产生影响。

线程:是一个过程中的不同执行门路。线程有本人的堆栈和局部变量,但线程之间没有独自的地址空间,一个线程死掉就等于整个过程死掉。

  •  简而言之, 一个程序至多有一个过程, 一个过程至多有一个线程.
  •  线程的划分尺度小于过程,使得多线程程序的并发性高。
  •  另外,过程在执行过程中领有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
  •  每个独立的线程有一个程序运行的入口、程序执行序列和程序的进口。然而线程不可能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行管制。
  •  从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行局部能够同时执行。但操作系统并没有将多个线程看做多个独立的利用,来实现过程的调度和治理以及资源分配。这就是过程和线程的重要区别

二、并发与并行

并发:是指同一个时间段内多个工作同时都在执行,并且都没有执行完结。并发工作强调在一个时间段内同时执行,而一个时间段由多个单位工夫累积而成,所以说并发的多个工作在单位工夫内不肯定同时在执行。

并行:是说在单位工夫内多个工作同时在执行。

在多线程编程实际中,线程的个数往往多于 CPU 的个数,所以个别都称多线程并发编程而不是多线程并行编程。

并发过程中常见的问题:

1、线程平安问题:

多个线程同时操作共享变量 1 时,会呈现线程 1 更新共享变量 1 的值,然而其余线程获取到的是共享变量没有被更新之前的值。就会导致数据不精确问题。

2、共享内存不可见性问题

Java 内存模型(解决共享变量)

Java 内存模型规定,将所有的变量都寄存在主内存中,当线程应用变量时,会把主内存外面的变量复制到本人的工作空间或者叫作工作内存,线程读写变量时操作的是本人工作内存中的变量。(如上图所示)

(理论工作的 java 内存模型)

上图中所示是一个双核 CPU 零碎架构,每个核有本人的控制器和运算器,其中控制器蕴含一组寄存器和操作控制器,运算器执行算术逻辅运算。CPU 的每个核都有本人的一级缓存,在有些架构外面还有一个所有 CPU 都共享的二级缓存。那么 Java 内存模型外面的工作内存,就对应这里的 Ll 或者 L2 缓存或者 CPU 的寄存器

  • 线程 A 首先获取共享变量 X 的值,因为两级 Cache 都没有命中,所以加载主内存中 X 的值,如果为 0。而后把 X = 0 的值缓存到两级缓存,线程 A 批改 X 的值为 1, 而后将其写入两级 Cache,并且刷新到主内存。线程 A 操作结束后,线程 A 所在的 CPU 的两级 Cache 内和主内存外面的 X 的值都是 l。
  • 线程 B 获取 X 的值,首先一级缓存没有命中,而后看二级缓存,二级缓存命中了,所以返回 X =1;到这里一切都是失常的,因为这时候主内存中也是 X =l。而后线程 B 批改 X 的值为 2,并将其寄存到线程 2 所在的一级 Cache 和共享二级 Cache 中,最初更新主内存中 X 的值为 2,到这里一切都是好的。
  • 线程 A 这次又须要批改 X 的值,获取时一级缓存命中,并且 X = l 这里问题就呈现了,明明线程 B 曾经把 X 的值批改为 2,为何线程 A 获取的还是 l 呢?这就是共享变量的内存不可见问题,也就是线程 B 写入的值对线程 A 不可见。

synchronized 的内存语义:

这个内存语义就能够解决共享变量内存可见性问题。进入 synchronized 块的内存语义是把在 synchronized 块内应用到的变量从线程的工作内存中革除,这样在 synchronized 块内应用到该变量时就不会从线程的工作内存中获取,而是间接从主内存中获取。退出 synchronized 块的内存语义是把在 synchronized 块内对共享变量的批改刷新到主内存。会造成上下文切换的开销,独占锁,升高并发性

Volatile 的了解:

该关键字能够确保对一个变量的更新对其余线程马上可见。当一个变量被申明为 volatile 时,线程在写入变量时不会把值缓存在寄存器或者其余中央,而是会把值刷新回主内存。当其余线程读取该共享变量时-,会从主内存从新获取最新值,而不是应用以后线程的工作内存中的值。volatile 的内存语义和 synchronized 有相似之处,具体来说就是,当线程写入了 volatile 变量值时就等价于线程退出 synchronized 同步块(把写入工作内存的变量值同步到主内存),读取 volatile 变量值时就相当于进入同步块(先清空本地内存变量值,再从主内存获取最新值)。不能保障原子性

三、创立线程

1、继承 Thread 类

重写 run 办法:应用继承形式的益处是,在 run()办法内获取以后线程间接应用 this 就能够了,毋庸应用 Thread.currentThread()办法;不好的中央是 Java 不反对多继承,如果继承了 Thread 类,那么就不能再继承其余类。另外工作与代码没有拆散,当多个线程执行一样的工作时须要多份工作代码。

public class ThreadRuning extends Thread{public ThreadRuning(String name){  
// 重写结构,能够对线程增加名字
        super(name);
    }
    @Override
    public void run() {while(true){System.out.println("good time");
// 在 run 办法里,this 代表以后线程
            System.out.println(this);
        }
    }
    public static void main(String[] args){ThreadRuning threadRuning = new ThreadRuning("1111");
        threadRuning.start();}
}

2、实现 Runable 接口

实现 run 办法:解决继承 Thread 的毛病,没有返回值

public class RunableTest implements Runnable {
    @Override
    public void run() {while (true) {System.out.println("good time");
        }
    }
    public static void main(String[] args) {RunableTest runableTest1 = new RunableTest();
        RunableTest runableTest2 = new RunableTest();
        new Thread(runableTest1).start();
        new Thread(runableTest1).start();
        new Thread(runableTest2).start();}
}

3、实现 Callable 接口

实现 call 办法:

public class CallTest implements Callable {
    @Override
    public Object call() throws Exception {return "hello world";}
 
    public static void main(String[] args){FutureTask<String> futureTask = new FutureTask<String>(new CallTest());
        new Thread(futureTask).start();
        try {String result = futureTask.get();
            System.out.println(result);
        } catch (InterruptedException e) {e.printStackTrace();
        } catch (ExecutionException e) {e.printStackTrace();
        }
    }
}

应用继承形式的益处是不便传参,你能够在子类外面增加成员变量,通过 set 办法设置参数或者通过构造函数进行传递,而如果应用 Runnable 形式,则只能应用主线程外面被申明为 final 的变量。不好的中央是 Java 不反对多继承,如果继承了 Thread 类,那么子类不能再继承其余类,而 Runable 则没有这个限度。前两种形式都没方法拿到工作的返回后果,然而 Callable 形式能够

四、Thread 类详解

线程个性:

1、线程能被标记为守护线程,也能够是用户线程

2、每个线程均调配一个 name,默认为(Thread- 自增数字)的组合

3、每个线程都有优先级. 高优先级线程优先于低优先级线程执行. 1-10,默认为 5

4、main 所在的线程组为 main,结构线程的时候没有事实的指定线程组,线程组默认和父线程一样

5、当线程中的 run()办法代码外面又创立了一个新的线程对象时, 新创建的线程优先级和父线程优先级一样.

6、当且仅当父线程为守护线程时, 新创建的线程才会是守护线程.

7、当 JVM 启动时, 通常会有惟一的一个非守护线程 (这一线程用于调用指定类的 main() 办法)

JVM 会继续执行线程直到上面状况某一个产生为止:

1)类运行时 exit()办法被调用 且 平安机制容许此 exit()办法的调用.

2)所有非守护类型的线程均曾经终止,or run()办法调用返回 or 在 run()办法内部抛出了一些可流传性的异样.

Init 办法:

/**
 * Initializes a Thread.
 * @param g 线程组
 * @param target 执行对象
 * @param name 线程名
 * @param stackSize 新线程栈大小,为 0 示意疏忽
 * @param acc 用于继承的访问控制上下文
 * @param inheritThreadLocals 如果值为 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();
   // 如果所属线程组为 null
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();
    /*
     * Do we have the required permissions?
     */
    if (security != null) {if (isCCLOverridden(getClass())) {security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }
    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);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;
 
    /* Set thread ID 设置线程 id*/
    tid = nextThreadID();}

构造方法:所有的构造方法都是调用 init()办法

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);
}
 
 
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,
              long stackSize) {init(group, target, name, stackSize);
}

线程状态:

public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }

NEW:状态是指线程刚创立, 尚未启动

RUNNABLE:状态是线程正在失常运行中, 当然可能会有某种耗时计算 /IO 期待的操作 /CPU 工夫片切换等, 这个状态下产生的期待个别是其余系统资源, 而不是锁, Sleep 等

BLOCKED:这个状态下, 是在多个线程有同步操作的场景, 比方正在期待另一个线程的 synchronized 块的执行开释, 或者可重入的 synchronized 块里他人调用 wait() 办法, 也就是这里是线程在期待进入临界区

WAITING:这个状态下是指线程领有了某个锁之后, 调用了他的 wait 办法, 期待其余线程 / 锁拥有者调用 notify / notifyAll 一遍该线程能够持续下一步操作, 这里要辨别 BLOCKED 和 WATING 的区别, 一个是在临界点里面期待进入, 一个是在了解点外面 wait 期待他人 notify, 线程调用了 join 办法 join 了另外的线程的时候, 也会进入 WAITING 状态, 期待被他 join 的线程执行完结

TIMED_WAITING:这个状态就是无限的 (工夫限度) 的 WAITING, 个别呈现在调用 wait(long), join(long)等状况下, 另外一个线程 sleep 后, 也会进入 TIMED_WAITING 状态

TERMINATED:这个状态下示意 该线程的 run 办法曾经执行结束了, 基本上就等于死亡了(过后如果线程被长久持有, 可能不会被回收)

(在很多文章中都写了 running 状态,其实源码外面只有六种的,当本人写一个线程通过 while 始终放弃执行状态,而后应用 jconsole 工具去查看线程的状态,的确是 Runable 状态)

Api 文档是这么说的:

其实咱们能够了解为两种状态,一个是 running,示意正在执行,一个是 runable,示意准备就绪了,只是在期待其余的系统资源。而后咱们就能够了解如下图

Start 办法:

public synchronized void start() {
        /**
         * 此办法并不会被次要办法线程 or 由虚拟机创立的零碎组线程所调用.
         * 任何向此办法增加的新性能办法在将来都会被增加到虚拟机中.
         * 0 状态值代表了 NEW 的状态.
         */
        if (threadStatus != 0) // 线程不能反复 start
            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. */
        group.add(this);
 
        boolean started = false;
        try {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 */
            }
        }
    }
 
    private native void start0();

是一个本地办法,提醒线程调度器以后线程违心放弃以后 CPU 的应用。如果以后资源不缓和,调度器能够疏忽这个提醒。实质上线程状态始终是 RUNNABLE, 然而我能够了解为 RUNNABLE 到 RUNNING 的转换

sleep 办法:

/**
     * 此办法会引起以后执行线程 sleep(长期进行执行)指定毫秒数.
     * 此办法的调用不会引起以后线程放弃任何监听器 (monitor) 的所有权(ownership).
     */
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);
}

sleep 办法,有一个重载办法,sleep 办法会开释 cpu 的工夫片,然而不会开释锁,调用 sleep()之后从 RUNNABLE 状态转为 TIMED_WAITING 状态

join 办法 

/**
     * 最多期待参数 millis(ms)时长以后线程就会死亡. 参数为 0 时则要继续期待.
     * 此办法在实现上: 循环调用以 this.isAlive()办法为条件的 wait()办法.
     * 当线程终止时 notifyAll()办法会被调用.
     * 倡议应用程序不要在线程实例上应用 wait,notify,notifyAll 办法.
     */
    public final synchronized void join(long millis)
            throws InterruptedException {long base = System.currentTimeMillis();
        long now = 0;
 
        // 如果等待时间 <0, 则抛出异样
        if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");
        }
 
        // 如果等待时间为 0
        if (millis == 0) {while (isAlive()) {wait(0);
            }
        } else {while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {break;}
                wait(delay);
                now = System.currentTimeMillis() - base;}
        }
    }
 
    // 等待时间单位为纳秒, 其它解释都和下面办法一样
    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);
    }
 
    // 办法性能: 期待始终到线程死亡.
    public final void join() throws InterruptedException {join(0);
    }

join 某个线程 A,会使得线程 B 进入期待,晓得线程 A 完结,或者达到给定的工夫,那么期间线程 B 处于 BLOCKED 的状态,而不是线程 A

五、其余办法

接下来聊一下 Object 类的 wait,notify 和 notifyAll 办法

wait 办法

public final native void wait(long timeout) throws InterruptedException; // 本地办法 参数为毫秒
public final void wait(long timeout, int nanos) throws InterruptedException {// 参数为毫秒和纳秒
        if (timeout < 0) {throw new IllegalArgumentException("timeout value is negative");
        }
 
        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException("nanosecond timeout value out of range");
        }
 
        if (nanos > 0) {timeout++;}
 
        wait(timeout);
    }
    public final void wait() throws InterruptedException {wait(0);
    }

可见 wait()和 wait(long timeout, int nanos)都在在外部调用了 wait(long timeout)办法。

上面次要是说说 wait(long timeout)办法

wait 办法会引起以后线程阻塞,直到另外一个线程在对应的对象上调用 notify 或者 notifyAll()办法,或者达到了办法参数中指定的工夫。

调用 wait 办法的以后线程肯定要领有对象的监视器锁。

wait 办法会把以后线程 T 搁置在对应的 object 上的期待队列中,在这个对象上的所有同步申请都不会失去响应。线程调度将不会调用线程 T,在以下四件事产生之前,线程 T 会被唤醒(线程 T 是在其代码中调用 wait 办法的那个线程)

1、当其余的线程在对应的对象上调用 notify 办法,而在此对象的对应的期待队列中将会任意抉择一个线程进行唤醒。

2、其余的线程在此对象上调用了 notifyAll 办法

3、其余的线程调用了 interrupt 办法来中断线程 T

4、期待的工夫曾经超过了 wait 中指定的工夫。如果参数 timeout 的值为 0,不是指实在的等待时间是 0,而是线程期待直到被另外一个线程唤醒为止。

被唤醒的线程 T 会被从对象的期待队列中移除并且从新可能被线程调度器调度。之后,线程 T 会像平时一样跟其余的线程竞争获取对象上的锁;一旦线程 T 取得了此对象上的锁,那么在此对象上的所有同步申请都会复原到之前的状态,也就是复原到 wait 被调用的状况下。而后线程 T 从 wait 办法的调用中返回。因而,当从 wait 办法返回时,对象的状态以及线程 T 的状态跟 wait 办法被调用的时候一样。

线程在没有被唤醒,中断或者工夫耗尽的状况下依然可能被唤醒,这叫做 伪唤醒。尽管在理论中,这种状况很少产生,然而程序肯定要测试这个可能唤醒线程的条件,并且在条件不满足时,线程持续期待。换言之,wait 操作总是呈现在循环中,就像上面这样:

synchronized(对象){while(条件不满足){对象.wait();
  }
  对应的逻辑解决
}

    如果以后的线程被其余的线程在以后线程期待之前或者正在期待时调用了 interrupt()中断了,那么会抛出 InterruptedExcaption 异样。直到这个对象下面的锁状态复原到下面形容的状态以前,这个异样是不会抛出的。

     

 要留神的是,wait 办法把以后线程搁置到这个对象的期待队列中,解锁也仅仅是在这个对象上;以后线程在其余对象下面上的锁在以后线程期待的过程中依然持有其余对象的锁。

   

这个办法应该仅仅被持有对象监视器的线程调用。

   

wait(long timeout, int nanos)办法的实现中只有 nanos 大于 0,那么 timeout 工夫就加上一毫秒,次要是更准确的管制工夫,其余的跟 wait(long timeout)一样

notify 办法

public final native void notify(); // 本地办法

告诉可能期待该对象的对象锁的其余线程。由 JVM(与优先级无关)随机筛选一个处于 wait 状态的线程。
 在调用 notify()之前,线程必须取得该对象的对象级别锁 
 执行完 notify()办法后,不会马上开释锁,要直到退出 synchronized 代码块,以后线程才会开释锁 
 notify()一次只随机告诉一个线程进行唤醒

notifyAll()办法

public final native void notifyAll();// 本地办法

和 notify()差不多,只不过是使所有正在期待池中期待同一共享资源的全副线程从期待状态退出,进入可运行状态 
让它们竞争对象的锁,只有取得锁的线程能力进入就绪状态 
每个锁对象有两个队列:就绪队列和阻塞队列 

  • 就绪队列:存储将要取得锁的线程 
  • 阻塞队列:存储被阻塞的线程

六、实例

1、sleep

public class ThreadDemo1 {public static void main(String[] args) {MyThread mt = new MyThread();
 
        // 举荐
        MyRunnable mr = new MyRunnable();
        Thread t2 = new Thread(mr);
 
        mt.start();// 启动线程
        t2.start();
 
 
        for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "-" + i);
            try {Thread.sleep(500);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }
 
    }
}
 
/**
 * 实现线程的第一种形式:继承 thread 类
 */
class MyThread extends Thread {
    @Override
    public void run() {for (int i = 0; i < 100; i++) {if (this.isInterrupted()) {break;}
            System.out.println(Thread.currentThread().getName() + "-" + i);
            try {Thread.sleep(500);
            } catch (InterruptedException e) {e.printStackTrace();
                this.interrupt();}
 
        }
    }
}
 
/**
 * 实现线程的第二种形式:实现 Runnable 接口
 */
class MyRunnable implements Runnable {
    @Override
    public void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "-" + i);
            try {Thread.sleep(500);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }
    }
}

2、join 和中断(举荐用标记中断)

public class ThreadDemo2 {public static void main(String[] args){MyRunable2 mr2 = new MyRunable2();
        Thread t = new Thread(mr2);
//        t.start();
 
        MyRunable3 mr3 = new MyRunable3();
        Thread t2 = new Thread(mr3);
        t2.start();
 
        for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName()+"--"+i);
            try {Thread.sleep(300);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            if(i==20){
//                try {  // 这些关上用来测试 join
//                    t.join();// 让 t 线程执行结束
//                } catch (InterruptedException e) {//                    e.printStackTrace();
//                }
//                t.interrupt();// 中断线程, 只是作了一个中断标记,用于测试 interrupt 办法
                mr3.flag = false; // 用于测试标记中断
            }
        }
    }
}
 
class MyRunable2 implements Runnable{
    @Override
    public void run() {for (int i = 0; i < 50; i++) {if(Thread.interrupted()){// 测试中断状态,此办法会把中断状态革除
                //....
                break;
            }
            System.out.println(Thread.currentThread().getName()+"--"+i);
            try {Thread.sleep(300);
            } catch (InterruptedException e) {e.printStackTrace();
                Thread.currentThread().interrupt();
            }
        }
    }
}
<br>// 标记中断
class MyRunable3 implements Runnable{
    public boolean flag = true;
    public MyRunable3(){flag = true;}
    @Override
    public void run() {
        int i=0;
        while(flag){System.out.println(Thread.currentThread().getName()+"==="+(i++));
            try {Thread.sleep(300);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }
    }
}

3、优先级和守护过程

public class ThreadDemo3 {public static void main(String[] args){MyRunnable4 mr4 = new MyRunnable4();
        Thread t = new Thread(mr4);
        t.setName("Thread-t");
        // 优先级高能够进步该线程抢点 CPU 工夫片的概率大
        t.setPriority(Thread.MAX_PRIORITY);
        // 线程能够分成守护线程和 用户线程,当过程中没有用户线程时,JVM 会退出
        t.setDaemon(true);// 把线程设置为守护线程
        System.out.println(t.isAlive());
        t.start();
        System.out.println(t.isAlive());
 
        for (int i = 0; i < 50; i++) {System.out.println("main--"+i);
            try {Thread.sleep(200);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            if (i==5){Thread.yield();// 让出本次 CPU 执行工夫片
            }
        }
 
 
    }
}
 
 
class MyRunnable4 implements Runnable{
    @Override
    public void run() {for (int i = 0; i < 50; i++) {System.out.println("--"+i);
            try {Thread.sleep(500);
            } catch (InterruptedException e) {e.printStackTrace();
            }
        }
    }
}

4、生产者与消费者(遗记在哪篇文章看到的了。。道歉)

定义一个接口:

package threadtest.procon;
 
public interface AbstractStorage {void consume(int num);
    void product(int num);
}

定义一个类实现接口,用于寄存生产的货色

package threadtest.procon;
 
import java.util.LinkedList;
 
/**
 * @author: LUGH1
 * @date: 2019-7-4
 * @description:
 */
public class Storage implements AbstractStorage{
    private final int MAX_SIZE = 100;
    private LinkedList list = new LinkedList();
 
    @Override
    public void consume(int num) {synchronized (list){while (list.size()<num){System.out.println("【要生产的产品数量】:" + num + "\t【库存量】:"+ list.size() + "\t 临时不能执行生产工作!");
                try {list.wait(2000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
            }
 
            for(int i=0;i<num;i++){list.remove();
            }
            System.out.println("【曾经生产产品数】:" + num + "\t【现仓储量为】:" + list.size());
 
            list.notifyAll();}
    }
 
    @Override
    public void product(int num) {synchronized (list){while(list.size()+num > MAX_SIZE){System.out.println("【要生产的产品数量】:" + num + "\t【库存量】:" + list.size() + "\t 临时不能执行生成工作!");
                try {list.wait(2000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
 
            }
            for(int i=0;i<num;i++){list.add(new Object());
            }
 
            System.out.println("【曾经生产产品数】:" + num + "\t【现仓储量为】:" + list.size());
            list.notifyAll();}
    }
}

生产者类:

package threadtest.procon;
 
/**
 * @author: LUGH1
 * @date: 2019-7-4
 * @description:
 */
public class Producer extends Thread {
    private int num;
    public AbstractStorage abstractStorage;
 
    public Producer(AbstractStorage abstractStorage){this.abstractStorage = abstractStorage;}
 
    public void setNum(int num) {this.num = num;}
 
    public void produce(int num){abstractStorage.product(num);
    }
 
    @Override
    public void run() {produce(num);
    }
}

消费者类:

package threadtest.procon;
 
/**
 * @author: LUGH1
 * @date: 2019-7-4
 * @description:
 */
public class Consumer extends Thread {
    private int num;
    public AbstractStorage abstractStorage;
 
    public Consumer(AbstractStorage abstractStorage){this.abstractStorage = abstractStorage;}
 
    public void setNum(int num){this.num = num;}
    public void consume(int num){this.abstractStorage.consume(num);
    }
 
    @Override
    public void run() {consume(num);
    }
}

测试类:

package threadtest.procon;
 
/**
 * @author: LUGH1
 * @date: 2019-7-4
 * @description:
 */
public class Test {public static void main(String[] args){AbstractStorage abstractStorage = new Storage();
 
        // 生产者对象
        Producer p1 = new Producer(abstractStorage);
        Producer p2 = new Producer(abstractStorage);
        Producer p3 = new Producer(abstractStorage);
        Producer p4 = new Producer(abstractStorage);
        Producer p5 = new Producer(abstractStorage);
        Producer p6 = new Producer(abstractStorage);
        Producer p7 = new Producer(abstractStorage);
 
        // 消费者对象
        Consumer c1 = new Consumer(abstractStorage);
        Consumer c2 = new Consumer(abstractStorage);
        Consumer c3 = new Consumer(abstractStorage);
 
        // 设置生产者产品生产数量
        p1.setNum(10);
        p2.setNum(20);
        p3.setNum(30);
        p4.setNum(40);
        p5.setNum(30);
        p6.setNum(20);
        p7.setNum(80);
 
        // 设置消费者产品生产数量
        c1.setNum(50);
        c2.setNum(70);
        c3.setNum(20);
 
        c1.start();
        c2.start();
        c3.start();
 
        p1.start();
        p2.start();
        p3.start();
        p4.start();
        p5.start();
        p6.start();
        p7.start();}
 
}

正文完
 0