Posts Java 线程的基本使用
Post
Cancel

Java 线程的基本使用

创建线程

创建线程的方式有两种:

  • 继承 Thread
  • 实现 Runnable 接口

Thread 类实现了 Runnable 接口。使用继承 Thread 类的方式创建线程时,最大的局限是不支持多继承。所以为了支持多继承,应该使用实现 Runnable 接口的方式。两种方式创建的线程在工作时是一样的,没有本质区别。

第一种方式,继承 Thread 类并重写 run() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Work extends Thread {
    
    @Override
    public void run() {
        System.out.println("Working...");
    }
}

public class Run {
    public static void main(String[] args) {
        Work work = new Work();
        work.start();
        System.out.println("End!");
    }
}

运行结果可能 “End!”先输出。在使用多线程时,运行结果与调用顺序是无关的。

调用 run() 方法只是普通的方法调用,不会启动线程。如果多次调用 start() 方法,会抛出 IllegalThreadStateException 异常。

第二种方式,实现 Runnable 接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Work implements Runnable {
    
    @Override
    public void run() {
        System.out.println("Working...");
    }
}

public class Run {
    public static void main(String[] args) {
        Thread t = new Thread(new Work());
        t.start();
        System.out.println("End!");
    }
}

这种方式与第一种在运行上没有什么区别。其优点在于突破了单继承的限制。

Thread 类的部分构造方法:

构造方法说明
Thread()创建一个新的线程
Thread(String name)创建一个新的线程,并指定名称
Thread(Runnable target)创建一个新的线程,将 target 作为运行对象
Thread(Runnable target, String name)将 target 作为运行对象,并指定名称
Thread(ThreadGroup group, Runnable target)将 target 作为运行对象,并作为线程组的一员

线程的方法

currentThread() 方法

currentThread() 方法返回正在被执行的线程的信息。

1
2
3
4
5
public class Run() {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
    }
}

以上代码在控制台输出 “main“,说明该方法被名为 main 的线程调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import static java.lang.System.out;

public class Run {

    static class Work extends Thread {
    
        @Override
        public void run() {
            out.printf("%s 被调用\n", currentThread().getName());
        }
    }

    public static void main(String[] args) {
        Work t1 = new Work(),
                t2 = new Work();
        t1.start();
        t2.start();
    }
}

以上代码运行结果:

1
2
3
4
Thread-0 被调用
Thread-1 被调用

Process finished with exit code 0

run() 方法中可以省略 Thread 直接调用 currentThread() 方法。

isAlive() 方法

该方法判断当前线程是否处于活动状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import static java.lang.System.out;

public class Run {

    static class Work extends Thread {
    
        @Override
        public void run() {
            out.printf("运行中 %s\n", isAlive());
        }
    }

    public static void main(String[] args) throws Throwable {
        Work t = new Work();
        out.printf("运行前: %s\n", t.isAlive());
        t.start();
        // 等待线程运行完成
        Thread.sleep(1000);
        out.printf("运行结束: %s\n", t.isAlive());
    }
}

以上代码运行结果:

1
2
3
4
5
运行前: false
运行中 true
运行结束: false

Process finished with exit code 0

sleep() 方法

sleep() 方法指定毫秒数让当前线程休眠(暂停运行),该操作不会释放锁

停止线程

interrupt() 方法

interrupt() 方法并不能立刻停止线程,只是在在线程中打了一个停止的标记。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {

        @Override
        public void run() {
            for (int i = 1; i <= 50000; i++) {
                out.printf("i = %d\n", i);
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(200);
        work.interrupt();
        out.println("Call interrupt!");
    }
}

以上代码运行结果:

1
2
3
4
5
6
7
8
9
...
i = 8190
i = 8191
i = 8192
Call interrupt!
i = 8193
i = 8194
i = 8195
...

interrupt() 方法调用后,线程仍在运行。

要使用 interrupt() 方法停止线程,需要在线程中判断中断状态,有两个方法:

  • interrupted():测试当前线程是否是中断状态,执行后将状态清除,设置为 false
  • isInterrupted():作用同上,但是不清除状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {

        @Override
        public void run() {
            for (int i = 1; i <= 50000; i++) {
                if (isInterrupted()) {
                    out.println("跳出循环!");
                    break;
                }
                out.printf("i = %d\n", i);
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(200);
        work.interrupt();
        out.println("Call interrupt!");
    }
}

以上代码执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
...
i = 8301
i = 8302
i = 8303
i = 8304
i = 8305
i = 8306
i = 8307
Call interrupt!
跳出循环!

Process finished with exit code 0

在调用 interrupt() 方法后,循环已经退出。但是这种方式只是跳出了循环,假如 for 循环外还有代码,仍然会执行。

抛出异常停止线程

可以在判断线程状态为中断时,抛出一个异常,在 catchfinally 块中做中断后的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {

        @Override
        public void run() {
            try {
                for (int i = 1; i <= 50000; i++) {
                    if (isInterrupted()) {
                        out.println("Interrupted!");
                        throw new InterruptedException("抛出异常!");
                    }
                    out.printf("i = %d\n", i);
                }
                out.println("for 循环结束!");
            } catch (InterruptedException e) {
                out.println(e.getMessage());
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(200);
        work.interrupt();
        out.println("Call interrupt!");
    }
}

以上代码将线程要执行的任务放入 try 块中,当判断为中断状态时,抛出 InterruptedException ,如果需要释放锁,可以在 finally 块中执行。

也可以配合 return 来停止线程:

1
2
3
if (isInterrupted()) {
    return;
}

暂停线程

Java 提供了 suspend()resume() 方法来暂停和恢复线程,不过这两个方法已经过期作废了。

suspend() 方法暂停线程时,不会释放锁。所以使用 suspend() 方法容易产生死锁。

如果需要暂停线程,可以加入一个标记,若标记指出线程需要暂停,使用 wait() 进入等待状态,如需要恢复,使用 notify() 唤醒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {
        // 暂停标记
        private boolean isSuspended = false;

        void pause() {
            isSuspended = true;
        }

        synchronized void wake() {
            isSuspended = false;
            // 唤醒
            this.notify();
            out.println("已唤醒!");
        }

        @Override
        public void run() {
            synchronized (this) {
                try {
                    for (int i = 1; i <= 5000; i++) {
                        if (isInterrupted()) {
                            return;
                        }

                        if (isSuspended) {
                            out.println("已暂停!");
                            // 等待
                            this.wait();
                        }

                        out.printf("%s i = %d\n", getName(), i);
                    }
                    out.printf("%s end!\n", getName());
                } catch (InterruptedException e) {
                    out.println(e.getMessage());
                }
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(100);
        // 暂停
        work.pause();
        Thread.sleep(100);
        // 唤醒
        work.wake();
    }
}

以上代码使用 wait()notify() 暂停与恢复线程。运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
Thread-0 i = 202
Thread-0 i = 203
Thread-0 i = 204
已暂停!
已唤醒!
Thread-0 i = 205
Thread-0 i = 206
Thread-0 i = 207
...
Thread-0 i = 4998
Thread-0 i = 4999
Thread-0 i = 5000
Thread-0 end!

Process finished with exit code 0

yield 方法

yield() 方法的作用是放弃当前的 CPU 资源,让其他的任务去占用 CPU 执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得时间片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import static java.lang.System.currentTimeMillis;
import static java.lang.System.out;

public class Yield {
    static class Work extends Thread {

        @Override
        public void run() {
            long before = currentTimeMillis();
            int sum = 0;
            for (int i =1; i < 2000000; i++) {
                // yield();
                sum += (i + 1);
            }
            long after = currentTimeMillis();
            out.printf("Cost: %dms\n", after - before);
        }
    }

    public static void main(String[] args) {
        new Work().start();
    }
}

以上代码不使用 yield() 方法时大概 15ms 执行完,加上后大概有 500ms。

OLDER POST NEWER POST

Comments powered by Disqus.

内容

Search Results