欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java 线程的基本使用

程序员文章站 2023-11-08 16:15:10
创建线程 创建线程的方式有两种: 继承 类 实现 接口 类实现了 接口。 使用继承 类的方式创建线程时,最大的局限是不支持多继承 。所以为了支持多继承,应该使用实现 接口的方式。两种方式创建的线程在工作时是一样的,没有本质区别。 第一种方式,继承 类并重写 方法: 运行结果可能 “End!”先输出。 ......

创建线程

创建线程的方式有两种:

  • 继承 thread
  • 实现 runnable 接口

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

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

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 接口:

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() 方法返回正在被执行的线程的信息。

public class run() {
    public static void main(string[] args) {
        system.out.println(thread.currentthread().getname());
    }
}

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

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

以上代码运行结果:

thread-0 被调用
thread-1 被调用

process finished with exit code 0

run() 方法中可以省略 thread 直接调用 currentthread() 方法。

isalive() 方法

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

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

以上代码运行结果:

运行前: false
运行中 true
运行结束: false

process finished with exit code 0

sleep() 方法

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

停止线程

interrupt() 方法

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

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!");
    }
}

以上代码运行结果:

...
i = 8190
i = 8191
i = 8192
call interrupt!
i = 8193
i = 8194
i = 8195
...

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

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

  • interrupted():测试当前线程是否是中断状态,执行后将状态清除,设置为 false
  • isinterrupted():作用同上,但是不清除状态。
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!");
    }
}

以上代码执行结果:

...
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 块中做中断后的处理:

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 来停止线程:

if (isinterrupted()) {
    return;
}

暂停线程

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

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

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

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() 暂停与恢复线程。运行结果:

...
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 执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得时间片。

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。