精华内容
下载资源
问答
  • Runnable的代码可以被个线程(Thread实例)共享,适合与个线程处理同一资源的情况。首先我们来看下代码:这是实现Runnable接口的方式class MyRunnable implements Runnable {private int ticketsCont = ...

    Thread和Runnable两种方式的比较

    Runnable方式可以避免Thread方式由于Java单继承特性带来的缺陷。

    Runnable的代码可以被多个线程(Thread实例)共享,适合与多个线程处理同一资源的情况。

    首先我们来看下代码:

    这是实现Runnable接口的方式

    class MyRunnable implements Runnable {

    private int ticketsCont = 5;

    @Override

    public void run() {

    while (ticketsCont > 0) {

    ticketsCont--;

    System.out.println(Thread.currentThread().getName() + "卖了1张票,剩余的票数为:" + ticketsCont);

    }

    }

    }

    public class TicketsRunnable {

    public static void main(String[] args) {

    MyRunnable myRunnable = new MyRunnable();

    Thread thread1 = new Thread(myRunnable, "窗口1");

    Thread thread2 = new Thread(myRunnable, "窗口2");

    Thread thread3 = new Thread(myRunnable, "窗口3");

    thread1.start();

    thread2.start();

    thread3.start();

    }

    }

    //运行结果:

    //窗口1卖了1张票,剩余的票数为:4

    //窗口1卖了1张票,剩余的票数为:3

    //窗口1卖了1张票,剩余的票数为:2

    //窗口1卖了1张票,剩余的票数为:1

    //窗口1卖了1张票,剩余的票数为:0

    这是继承Thread类的方式

    class MyThread extends Thread {

    //火车票的总数

    private int ticketsCont = 5;

    //线程的名字

    private String name;

    public MyThread(String name) {

    this.name = name;

    }

    @Override

    public void run() {

    while (ticketsCont > 0) {

    ticketsCont--;

    System.out.println(name + "卖了一张票,剩余的票数为:" + ticketsCont);

    }

    }

    }

    public class TicketsThread {

    public static void main(String[] args) {

    MyThread t1 = new MyThread("窗口1");

    MyThread t2 = new MyThread("窗口2");

    MyThread t3 = new MyThread("窗口3");

    t1.start();

    t2.start();

    t3.start();

    }

    }

    //运行结果:

    //窗口1卖了一张票,剩余的票数为:4

    //窗口1卖了一张票,剩余的票数为:3

    //窗口1卖了一张票,剩余的票数为:2

    //窗口1卖了一张票,剩余的票数为:1

    //窗口1卖了一张票,剩余的票数为:0

    //窗口3卖了一张票,剩余的票数为:4

    //窗口3卖了一张票,剩余的票数为:3

    //窗口3卖了一张票,剩余的票数为:2

    //窗口3卖了一张票,剩余的票数为:1

    //窗口3卖了一张票,剩余的票数为:0

    //窗口2卖了一张票,剩余的票数为:4

    //窗口2卖了一张票,剩余的票数为:3

    //窗口2卖了一张票,剩余的票数为:2

    //窗口2卖了一张票,剩余的票数为:1

    //窗口2卖了一张票,剩余的票数为:0

    运行得出结果,你就会发现两种方式的不同:

    继承Thread类的方式会启动三个线程,每个线程都会卖5张票。

    实现Runnable接口的会启动三个线程,三个线程共享一个资源。也就是三个线程卖5张票。

    出现这种情况的原因是两种不同的线程实现方式本身就决定了其是否能进行资源共享:

    Thread:

    一个线程只能启动一次,通过Thread实现线程时,线程和线程所要执行的任务是捆绑在一起的。

    也就使得一个任务只能启动一个线程,不同的线程执行的任务是不相同的,所以两个线程之间是不能共享资源的,也没必要。

    当然如果一定要Thread的实现资源共享,那么可以在共享变量加上static关键字。

    Runnable:

    一个任务可以启动多个线程,通过Runnable方式实现的线程,实际是开辟一个线程,将任务传

    递进去,由此线程执行。可以实例化多个 Thread对象,将同一任务传递进去,也就是一个任务可以

    启动多个线程来执行它。这些线程执行的是同一个任务,所以他们的资源是共享。

    所以说Runnable适合多个线程处理同一个资源的情况。

    线程的生命周期

    53556419aeeb76e5be69690812627797.png

    创建:新建一个新的线程对象,如Thread thread=new Thread();

    就绪:创建了线程对象后,调用了线程的start()方法(注意:此时线程只是进入了线程队列,等待获取cpu

    服务,具备了运行的条件,但是并不一定已经开始运行了)。

    运行处于就绪状态的线程,一旦获取了cpu资源,便进入到了运行状态,开始执行run()方法里面的逻辑。

    终止:线程的run()方法中的逻辑执行完毕,或者线程调用了stop()方法,线程便进入终止状态。

    阻塞:一个正在执行的线程在某些情况下,由于某种原因而暂时让出了cpu资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法。

    守护线程

    Java线程有两类

    用户线程:运行在前台,执行具体的任务,如任务的主线程,连接网络的子线程等都是用户线程。

    守护线程: 运行在后台,为其他的前台线程服务

    特点:一旦所有的用户线程都运行结束,守护线程会随着一起结束工作。

    应用:数据库连接池中的监测线程,JVM虚拟机启动后的监测线程

    最常见的守护线程:垃圾回收线程

    直接上代码:

    class DaemonRunnable implements Runnable {

    private int count;

    @Override

    public void run() {

    System.out.println("进入守护线程" + Thread.currentThread().getName());

    try {

    writeToFile();

    } catch (Exception e) {

    e.printStackTrace();

    }

    System.out.println("退出守护线程" + Thread.currentThread().getName());

    }

    private void writeToFile() throws Exception {

    File filename=new File("E:"+File.separator+"Daemon.txt");

    OutputStream os = new FileOutputStream(String.valueOf(filename), true);

    int count = 0;

    while (count < 999) {

    os.write(("\r\n world" + count).getBytes());

    System.out.println("守护线程" + Thread.currentThread().getName() + "向文件中写入了world" + count++);

    Thread.sleep(1000);

    }

    }

    }

    public class DaemonDemo {

    public static void main(String []args){

    System.out.println("进入主线程"+Thread.currentThread().getName());

    DaemonRunnable daemonRunnable=new DaemonRunnable();

    Thread thread= new Thread(daemonRunnable);

    thread.setDaemon(true);//设置为守护线程

    thread.start();

    Scanner scanner=new Scanner(System.in);

    scanner.next();

    System.out.println("退出主线程"+Thread.currentThread().getName());

    }

    }

    这时候通过的在Console中随便输入字符让主线程停止。

    进入主线程main

    进入守护线程Thread-0

    守护线程Thread-0向文件中写入了world0

    守护线程Thread-0向文件中写入了world1

    守护线程Thread-0向文件中写入了world2

    守护线程Thread-0向文件中写入了world3

    11

    退出主线程main

    可以看到用户线程停止的时候,守护线程也停止了。

    注意事项

    setDaemon(true)必须在start()方法之前调用,否则会抛出IllegalThreadStateException异常

    在守护线程中产生的新线程也是守护线程

    不是所有的任务都可以分配给守护线程来执行。比如读写操作或者逻辑计算

    使用jstack

    作用:生成jvm当前时刻线程的快照(threaddump,即当前进程中所有线程的信息)

    目的:帮助定位程序问题实现的原因,如长时间停顿,cpu占用率过高等。

    jstack是一个.exe的命令行程序,在jdk安装目录的bin目录下。

    我的jdk是默认安装在C盘的,所以路径是:C:\Program Files\Java\jdk1.8.0_131\bin。

    在bin目录下打开Terminal,输入jstack。

    C:\Program Files\Java\jdk1.8.0_131\bin>jstack

    Usage:

    jstack [-l]

    (to connect to running process)

    jstack -F [-m] [-l]

    (to connect to a hung process)

    jstack [-m] [-l]

    (to connect to a core file)

    jstack [-m] [-l] [server_id@]

    (to connect to a remote debug server)

    Options:

    -F to force a thread dump. Use when jstack does not respond (process is hung)

    -m to print both java and native frames (mixed mode)

    -l long listing. Prints additional information about locks

    -h or -help to print this help message

    可以看到jstack是通过pid码来获取线程快照信息的。

    我们可以打开任务管理器的 -> 详细服务,就可以看到当前正在运行的程序的pid码。

    然后我们启动主线程开始运行,在Terminal中输入:jstack -l 4528。

    4528是我当前运行的程序的pid码,如果没有相应的pid码,那么会提示拒绝访问。

    C:\Program Files\Java\jdk1.8.0_131\bin>jstack -l 804

    2017-06-24 11:01:25

    // 使用的Java虚拟机版本为Oracle的HotSpot 64位服务器版本

    Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode):

    "Thread-0" #11 daemon prio=5 os_prio=0 tid=0x00000000188ca800 nid=0x2560 waiting on condition [0x000000001941f000]

    java.lang.Thread.State: TIMED_WAITING (sleeping)

    at java.lang.Thread.sleep(Native Method)

    at Thread.demo.DaemonRunnable.writeToFile(DaemonDemo.java:32)

    at Thread.demo.DaemonRunnable.run(DaemonDemo.java:18)

    at java.lang.Thread.run(Thread.java:748)

    Locked ownable synchronizers:

    - None

    "Service Thread" #10 daemon prio=9 os_prio=0 tid=0x00000000187fc800 nid=0x252c runnable [0x0000000000000000]

    java.lang.Thread.State: RUNNABLE

    Locked ownable synchronizers:

    - None

    "C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x00000000187cf000 nid=0x2038 waiting on condition [0x0000000000000000]

    java.lang.Thread.State: RUNNABLE

    Locked ownable synchronizers:

    - None

    "C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000000018774800 nid=0x20c8 waiting on condition [0x0000000000000000]

    java.lang.Thread.State: RUNNABLE

    Locked ownable synchronizers:

    - None

    "C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000000018771800 nid=0x2298 waiting on condition [0x0000000000000000]

    java.lang.Thread.State: RUNNABLE

    Locked ownable synchronizers:

    - None

    "Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001876f800 nid=0x16e0 runnable [0x0000000018e1e000]

    java.lang.Thread.State: RUNNABLE

    at java.net.SocketInputStream.socketRead0(Native Method)

    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)

    at java.net.SocketInputStream.read(SocketInputStream.java:171)

    at java.net.SocketInputStream.read(SocketInputStream.java:141)

    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)

    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)

    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)

    - locked <0x00000000d6132b90> (a java.io.InputStreamReader)

    at java.io.InputStreamReader.read(InputStreamReader.java:184)

    at java.io.BufferedReader.fill(BufferedReader.java:161)

    at java.io.BufferedReader.readLine(BufferedReader.java:324)

    - locked <0x00000000d6132b90> (a java.io.InputStreamReader)

    at java.io.BufferedReader.readLine(BufferedReader.java:389)

    at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)

    Locked ownable synchronizers:

    - None

    "Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000017418000 nid=0x1404 waiting on condition [0x0000000000000000]

    java.lang.Thread.State: RUNNABLE

    Locked ownable synchronizers:

    - None

    "Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x00000000173ce800 nid=0x1b20 runnable [0x0000000000000000]

    java.lang.Thread.State: RUNNABLE

    Locked ownable synchronizers:

    - None

    "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000000288d800 nid=0xf64 in Object.wait() [0x000000001871f000]

    java.lang.Thread.State: WAITING (on object monitor)

    at java.lang.Object.wait(Native Method)

    - waiting on <0x00000000d5f08ec8> (a java.lang.ref.ReferenceQueue$Lock)

    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)

    - locked <0x00000000d5f08ec8> (a java.lang.ref.ReferenceQueue$Lock)

    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)

    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

    Locked ownable synchronizers:

    - None

    "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000002882000 nid=0x148c in Object.wait() [0x000000001861f000]

    java.lang.Thread.State: WAITING (on object monitor)

    at java.lang.Object.wait(Native Method)

    - waiting on <0x00000000d5f06b68> (a java.lang.ref.Reference$Lock)

    at java.lang.Object.wait(Object.java:502)

    at java.lang.ref.Reference.tryHandlePending(Reference.java:191)

    - locked <0x00000000d5f06b68> (a java.lang.ref.Reference$Lock)

    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

    Locked ownable synchronizers:

    - None

    "main" #1 prio=5 os_prio=0 tid=0x000000000085e800 nid=0x27d4 runnable [0x000000000215e000]

    java.lang.Thread.State: RUNNABLE

    at java.io.FileInputStream.readBytes(Native Method)

    at java.io.FileInputStream.read(FileInputStream.java:255)

    at java.io.BufferedInputStream.read1(BufferedInputStream.java:284)

    at java.io.BufferedInputStream.read(BufferedInputStream.java:345)

    - locked <0x00000000d5f5a498> (a java.io.BufferedInputStream)

    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)

    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)

    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)

    - locked <0x00000000d60ada70> (a java.io.InputStreamReader)

    at java.io.InputStreamReader.read(InputStreamReader.java:184)

    at java.io.Reader.read(Reader.java:100)

    at java.util.Scanner.readInput(Scanner.java:804)

    at java.util.Scanner.next(Scanner.java:1369)

    at Thread.demo.DaemonDemo.main(DaemonDemo.java:45)

    Locked ownable synchronizers:

    - None

    "VM Thread" os_prio=2 tid=0x0000000017386800 nid=0x1f68 runnable

    "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00000000027a7800 nid=0x2a24 runnable

    "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00000000027a9000 nid=0x71c runnable

    "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00000000027aa800 nid=0x16bc runnable

    "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00000000027ac000 nid=0x1d24 runnable

    "VM Periodic Task Thread" os_prio=2 tid=0x000000001882e000 nid=0x1e30 waiting on condition

    JNI global references: 33

    我就拿Thread-0这个线程的信息做解释:

    Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode)

    表示使用的是Oracle HotSpot64版本的JVM,为服务器版本,客户端版本为Client

    信息解释:

    "Thread-0" #11 daemon prio=5 os_prio=0 tid=0x00000000188ca800 nid=0x2560 waiting on condition [0x000000001941f000]

    java.lang.Thread.State: TIMED_WAITING (sleeping)

    at java.lang.Thread.sleep(Native Method)

    at Thread.demo.DaemonRunnable.writeToFile(DaemonDemo.java:32)

    at Thread.demo.DaemonRunnable.run(DaemonDemo.java:18)

    at java.lang.Thread.run(Thread.java:748)

    Locked ownable synchronizers:

    - None

    Thread-0表示这个线程的名字

    daemon表示该线程为守护线程

    prio=5表示线程的优先级

    tid和nid也是线程的16进制信息,结合其他指令可以很方便的定位出cpu占有率很高的线程

    java.lang.Thread.State: TIMED_WAITING (sleeping)表示线程的状态为TIMED_WAITING,这是调用了sleep()方法产生的结果

    需要注意的是,如果没有加上-l参数的话,Locked ownable synchronizers:- None这一段信息是不会显示出来的,这段表示的是锁的

    额外信息,None表示为没有。

    至于其他的线程信息的话,有兴趣的人可以去查阅相关的资料,在这里就不去做更多的介绍。

    展开全文
  • 启动,那么这个runnable对象还是在启动它的线程中执行的,并不是在独立线程中执行,所以我们可以把runnable对象在一个线程中执行,那么这些runnable就是一个一个执行的,既然是按顺序执行的,那么保留顺序信息...

    让线程依次执行的难点就是如何判断前一个线程执行结束。runnable对象一个特性就是如果是用runnable.run();启动,那么这个runnable对象还是在启动它的线程中执行的,并不是在独立线程中执行,所以我们可以把多个runnable对象在一个线程中执行,那么这些runnable就是一个一个执行的,既然是按顺序执行的,那么保留顺序信息就要用到queue对象了,我们建立一个queue对象保存这个顺序信息。请看示例代码

    首先创建一个类,这个类包含了一个queue对象

    class SerialExecutor {

    final Queue tasks = new LinkedBlockingQueue();

    final Executor executor;

    Runnable active;

    SerialExecutor(Executor executor) {

    this.executor = executor;

    }

    public void addrun(Runnable r) {

    tasks.add(r);

    }

    public void execute(final Runnable r) {

    try {

    r.run();

    } finally {

    scheduleNext();

    }

    }

    protected void scheduleNext() {

    if ((active = tasks.poll()) != null) {

    this.execute(active);

    }

    }

    }

    主类

    public class Demo {

    public static void main(String[] args) {

    final Executor executor = new Executor() {

    @Override

    public void execute(Runnable command) {

    // TODO Auto-generated method stub

    // new Thread(command).start();

    command.run();

    }

    };

    Runnable runnable1 = new Runnable() {

    @Override

    public void run() {

    // TODO Auto-generated method stub

    System.out.println("Runnable1");

    }

    };

    Runnable runnable2 = new Runnable() {

    @Override

    public void run() {

    // TODO Auto-generated method stub

    System.out.println("Runnable2");

    }

    };

    Runnable runnable3 = new Runnable() {

    @Override

    public void run() {

    // TODO Auto-generated method stub

    System.out.println("Runnable3");

    }

    };

    Runnable runnable4 = new Runnable() {

    @Override

    public void run() {

    // TODO Auto-generated method stub

    System.out.println("Runnable4");

    }

    };

    Runnable runnable5 = new Runnable() {

    @Override

    public void run() {

    // TODO Auto-generated method stub

    System.out.println("Runnable5");

    }

    };

    SerialExecutor se = new SerialExecutor(executor);

    se.addrun(runnable1);

    se.addrun(runnable2);

    se.addrun(runnable3);

    se.addrun(runnable4);

    se.addrun(runnable5);

    se.scheduleNext();

    这个流程就是在main类中创建5个Runnable对象,这5个Runnable对象分别加入SerialExecutor对象中的队列中,然后从队列中取出第一个对象,没有则结束方法。这个程序运行会按照12345的顺序打印Runnable。这个按顺序执行的原因就是因为Executor对象中的execute方法中是command.run(0)而不是new Thread(command).start(),这样每一个要执行的Runnable都会被阻塞在这一个一个执行。这个方法要注意不要在主线程中阻塞。

    还有一个方法就是用到synchronized关键字的特性,把执行语句写在用synchronized修饰的方法中,取出队列中的对象执行

    代码如下

    class SerialExecutor {

    final Queue tasks = new LinkedBlockingQueue();

    final Executor executor;

    Runnable active;

    SerialExecutor(Executor executor) {

    this.executor = executor;

    }

    public void addrun(Runnable r) {

    tasks.add(r);

    }

    public synchronized void execute(final Runnable r) {

    try {

    r.run();

    } finally {

    scheduleNext();

    }

    }

    protected void scheduleNext() {

    if ((active = tasks.poll()) != null) {

    this.execute(active);

    }

    }

    }

    展开全文
  • 通过实现 java.lang.Runnable 接口可实现创建线程创建线程步骤创建一个 Runnable 接口的实现类在实现类中重写 Runnable 接口的 run 方法,设置线程任务创建一个 Runnable 接口的实现类对象创建 Thread 类对象, ...

    通过实现 java.lang.Runnable 接口可实现创建多线程

    创建多线程步骤

    创建一个 Runnable 接口的实现类

    在实现类中重写 Runnable 接口的 run 方法,设置线程任务

    创建一个 Runnable 接口的实现类对象

    创建 Thread 类对象, 构造方法中传递 Runnable 接口的实现类对象

    调用 Thread 类中的 start 方法,开启新的线程执行run方法

    public class RunnableImpl implements Runnable {

    @Override

    public void run() {

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

    System.out.println(Thread.currentThread().getName() + "->" + i);

    }

    }

    }

    public class Test {

    public static void main(String[] args) {

    RunnableImpl run = new RunnableImpl();

    // 创建 Thread 类对象

    Thread t1 = new Thread(run);

    t1.start();

    }

    }

    结果:

    Thread-0->0

    Thread-0->1

    Thread-0->2

    使用 Runnable 的好处

    与继承 Thread 类相比,实现 Runnable 接口有以下好处:

    避免了单继承的局限性

    一个类只能继承一个类, 类继承了Thread类就不能继承其他的类

    实现了Runnable接口,还可以继承其他的类,实现其他的接口

    增强了程序的扩展性,降低了程序的耦合性(解耦)

    实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)

    实现类中,重写了run方法: 用来设置线程任务

    创建Thread类对象,调用start方法: 用来开启新线程

    展开全文
  • 在 Java 中线程的生命周期中一共有 6 种状态:New(新创建)Runnable(可运行)Blocked(被阻塞)Waiting(等待)Timed Waiting(计时等待)Terminated(被终止)查看 Thread 类的源码时,内部还定义了这样一个枚举类。...

    线程有哪 6 种状态?

    人有生老病死。同样的,线程有自己的生命周期。在 Java 中线程的生命周期中一共有 6 种状态:New(新创建)

    Runnable(可运行)

    Blocked(被阻塞)

    Waiting(等待)

    Timed Waiting(计时等待)

    Terminated(被终止)

    查看 Thread 类的源码时,内部还定义了这样一个枚举类。这个枚举类定义的就是线程的状态,源码如下:

    public enum State {

    NEW,

    RUNNABLE,

    BLOCKED,

    WAITING,

    TIMED_WAITING,

    TERMINATED;

    }

    PS:线程在任何时刻只可能处于以上 6 种的其中 1 种状态,我们可以调用 getState () 查看线程的状态。

    线程是如何切换状态的?

    我们知道线程有 6 种状态。然而,它是如何切换的呢?狗哥根据自己的理解做了一张图,接下来将根据这张图详细了解下线程状态的切换。

    线程的 6 种状态

    NEW (新创建)

    先注意 NEW 状态:线程被 NEW 出来,但还没调用 start 方法时,就处于这种状态。一旦调用了 start 方法也就进入了 RUNNABLE 状态。

    RUNNABLE(可运行)

    处于 RUNNABLE 的线程,比较特殊。它还分两种状态:Running 和 Ready。也就是说,Java 中处于 Runnable 状态的线程有可能正在执行,也有可能没有正在执行,正在等待被分配 CPU 资源。

    因此,我们可以推断出,一个处于 Runnable 状态的线程,当它运行到任务的一半时,执行该线程的 CPU 被调度去做其他事情,则该线程暂时不运行。但是,它的状态依然不变,还是 Runnable,因为它有可能随时被调度回来继续执行任务。

    也就是说:处于 Runnable 状态的线程并不一定在运行。这点在面试中常问,小伙伴们要注意了。

    Blocked(被阻塞)

    再来看看最简单的 Blocked 状态,从 Runnable 进入 Blocked 只有一种可能:就是进入 synchronized 关键字保护的代码,但是没有获取到 monitor 锁。

    线程的 6 种状态

    再来,看图的右侧,Blocked ----> Runnable 状态:当处于 Blocked 状态的线程获取到锁。

    Waiting (等待)

    线程从 Runnable 进入 Blocked 状态,有三种可能性:没有设置 Timeout 参数的 Object.wait () 方法。

    没有设置 Timeout 参数的 Thread.join () 方法。

    LockSupport.park () 方法。

    上面我们知道,线程进入 Blocked 状态只可能是:进入 synchronized 保护的代码,但是没获取到 monitor 锁。然而,Java 中还有很多锁,比如:ReentrantLock。线程在获取这种锁时,没有抢到就会进入 Waiting 状态,因为它本质上是调用了 LockSupport.park () 方法。

    同样的,调用 Object.wait () 和 Thread.join () 也会让线程进入等待状态。

    Blocked 与 Waiting 的区别是:Blocked 在等待其他线程释放 monitor 锁,而 Waiting 则是在等待某个条件,比如 join 的线程执行完毕,或者是 notify ()/notifyAll ()。

    线程的 6 种状态

    看 Waiting 右侧,Waiting ----> Runnable:1、当执行了 LockSupport.unpark (),2、join 的线程运行结束,3、被中断

    看 Waiting 右侧,Waiting ----> Blocked:我们看图可以知道其他线程调用 notify () 或 notifyAll () 来唤醒处于 Waiting 的线程,它会直接进入 Blocked 状态。这是为什么?

    其他线程能调用 notify () 或 notifyAll () 试图唤醒 Waiting 状态线程,说明必须持有同一个 monitor 锁,也就是说处于 Waiting 的线程被唤醒后并不能马上抢到 monitor 锁,所以它必须先进入 Blocked 状态。而唤醒它的线程执行完毕释放锁后,它能抢到锁就从 Blocked 进入 Runnable 状态。

    Timed Waiting(计时等待)

    这种状态与 Waiting 状态的区别在于:有没有时间限制,Timed Waiting 会等待超时,由系统自动唤醒,或者在超时前被唤醒信号唤醒。

    有以下 5 种情况会让线程进入 Timed Waiting 状态:设置 Timeout 参数的 Thread.sleep (time) 方法。

    设置 Timeout 参数的 Object.wait (time) 方法。

    设置 Timeout 参数的 Thread.join (time) 方法。

    设置 Timeout 参数的 LockSupport.parkNanos (long nanos) 方法。

    设置 Timeout 参数的 LockSupport.parkUntil (long deadline) 方法。

    线程的 6 种状态

    看 Timed Waiting 右侧,Timed Waiting ----> Blocked:跟 Waiting 一样,其他线程执行 notify () 和 notifyAll (),当前线程也是先进入 Blocked 状态,而后视锁的获取情况再决定是否进入 Runnable 状态。

    另外,Timed Waiting ----> Runnable :1、当前线程的超时时间到了且能直接获取到锁,2、join 的线程运行结束,3、被中断,4、调用了 LockSupport.unpark (),这几种情况会直接恢复到 Runnable,而无需经历 Blocked 状态。

    Terminated(被终止)

    最后一种,想要进入终止状态就比较简单了,有三种情况:任务执行完毕,线程正常退出。

    出现一个没有捕获的异常(比如直接调用 interrupt () 方法)。

    总结线程的状态是需要按照箭头方向走,比如线程从 New 状态是不可以直接进入 Blocked 状态的,它需要先经历 Runnable 状态。

    线程生命周期不可逆:一旦进入 Runnable 状态就不能回到 New 状态;一旦被终止就不可能再有任何状态的变化。所以一个线程只能有一次 New 和 Terminated 状态,只有处于中间状态才可以相互转换。

    展开全文
  • Runnable接口新理解:线程与线程任务的关系 首先来回顾一下java线程的最基本的知识: 1、Runnable接口介绍 @FunctionInterface public interface Runnable{ public void run(); } Runnable接口只定义了...
  • 进程与线程首先来看百度百科关于进程的介绍:进程是一个具有独立...直观一点:windows的任务管理里面,我们看到的eclipse和360等等,都是进程。(想深入了解的可以看看计算机操作系统)什么是线程?线程是在一个进程...
  • 线程背景 使用线程,是为了提高程序的执行效率,使用线程,每个线程互不影响,...进程想要执行任务,就要依赖线程。进程中最小的执行单位,就是线程。 线程的三种实现方式 1.继承Thread类 运行MemberServic
  • Java创建线程线程一、线程优势二、如何实现java线程1.继承Thread类,重写run()方法。2.读入数据总结 线程 进程是指一段正在执行的程序,线程是指程序执行的最小单元。一个进程拥有个线程,各个线程之间...
  • 并行就是同一时间处理条命令,就是一个时间点可以处理任务;并发就是同一时间段处理条命令,虽然微观上分时交替执行,但之间的间隙人们感觉不到,宏观上还是个命令同时执行。可见,多核系统可以做到程序的...
  • 线程实现二:实现Runnable接口4. synchronized 同步代码块5. 同步方法6. 线程生命周期图 1. 线程概述 学习线程之前,我们先要了解几个关于线程有关的概念。 A:进程: 进程指正在运行的程序。确切的来说,当一...
  • JAVA下的线程,线程 1.基本概念:程序,进程,线程 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指==一段静态的代码,==静态对象 进程(process)是程序的一次执行过程,或是正在...
  • java创建线程的方式有许多种,这里简要做个梳理1. 继承Thread类继承java.lang.Thread类,创建本地线程的类,重载run()方法,调用Thread的方法启动线程。示例代码如下:MyThread.javapublic class MyThread ...
  • java线程编程之使用runnable接口创建线程1.将实现Runnable接口的类实例化。2.建立一个Thread对象,并将第一步实例化后的对象作为参数传入Thread类的构造方法。最后通过Thread类的start方法建立线程。下面的代码...
  • 具体而言,这里说的 Java 线程状态均来自于 Thread 类下的 State 这一内部枚举类中所定义的状态:public enum State {NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;}什么是 RUNNABLE?直接看...
  • Runnable

    2021-01-04 14:59:51
    @java @线程 Runnnable的用法 创建一个Runnable接口的实现类 package ThreadDemo01;/* @Author wanghongyuan @Date 2021/1/4 ...在实现类中重写Runnable接口的run方法,设置线程任务 @Override publ
  • 当问起java实现线程的两种主要方式时,我们通常会提到使用Runnable接口与继承Thread类来实现线程;但两者究竟是如何实现线程呢? 在我们查看各类博文时,为什么有人说说使用Runnable接口实现线程会进行资源...
  • 1.概念和示例代码进程是内存中...为了更有效的完成任务和利用CPU资源,需要线程操作,因而需要线程编程。Java线程编程的时候,可以继承于Thread对象,也可以实现Runnable接口。示例代码如下。publicclassMain...
  • 1.创建线程任务方式一:Runnable Runnable#run() 实现线程逻辑, 无返回值 public interface Runnable { public abstract void run(); } 2.创建线程任务方式一:Callable 2.1 Callable Callable#call() 实现...
  • 1引言当前C++、Delphi、Java等开发工具支持并发多任务处理。Java多线程机制实现了多任务在宏观上同时执行,提供了并发过程中临界资源保护的同步机制和通信机制。本文以Windows2000为平台,以JDK1.4为开发环境,对上述...
  • 线程中实现Runnable与继承Thread的区别 Runnable本质是先创建任务,然后通过线程来分配任务。 实现代码如下: public class Demo { public static void main(String[] args) throws IOException, ...
  • 线程:一个进程在执行中可以产生个线程,简单说一个线程就像是运行在进程中的“小进程”。各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。 优点:线程编程可以充分利用CPU的资源。 工作原理:JVM...
  • java对象占用堆内存,操作对象线程占用系统内存,根据JVM规定,一个线程默认最大栈大小是1M,这个栈大小内存空间是需要从系统内存中分配的,线程过多会消耗很内存 操作系统需要频繁的上下文切换,影响性能 为了...
  • 1、阐述要用线程,线程为什么极其重要 2、阐述 Thread 类及底层原理 3、阐述 Runnable 接口及底层原理
  • Runnable 与 Callable是什么? 在前面的文章叙述中,有一句很明确的表达,java中只有Thread对象代表一个线程对象, 一个线程像一个工人一样,按照我们提供的说明书一句一句的读,并按照说明书说的去做, Runnable 或 ...
  • 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发个线程,每条线程并行执行不同的任务线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。 一、线程是什么? 1.进程 是指一个内存中...
  • 通过实现Runnable接口,实例化Thread类在实际应用中,我们经常用到线程,如车站的售票系统,车站的各个售票窗口相当于各个线程。当我们做这个系统的时候可能会想到两种方式来实现,继承Thread类或实现Runnable接口...
  • 本课讲的是如何实现一个Runnable,在一个独立线程上运行Runnable.run()方法.Runnable对象执行特别操作有时叫作任务。Thread和Runnable都是基础的类,靠他们自己,能力有限。作为替代,Android有强大的基础类,像...
  • 安卓多任务实现的基本原理 一.基本概念 操作一些耗时操作时候,如I/O读写大文件,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR(应用程序无响应)的响应提示窗口,这个时候我们考虑使用Thread...
  • Runnable实现参数传递

    2021-03-08 15:07:54
    大家都知道Runnable()是无法传参数的,但是,有时候,我们提交任务的时候,是需要传递参数的,所以,为了解决这个问题,有以下的方法; 这是,一个参数传递的设置接口,里面有一个设置参数的方法 public interface ...
  • - 继上一次我们初识了线程 进程 线程等概念,也学会使用继承Thread类的方式来实现线程 - 本节将会帮助你了解... - 实现Runnable接口的方式 实现线程效果 - 实现Callable接口的方式 实现线程效果

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 153,426
精华内容 61,370
关键字:

多任务runnable