多线程基础

153 阅读6分钟

1. 继承 Thread 类

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println("MyThread run");
    }
}

public class Test {

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

2. 实现 Runnable 接口

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("MyRunnable run");
    }
}

public class Test {

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

3. currentThread() 方法

currentThread() 方法可以返回代码段正在被哪个线程调用的信息

public class MyThread extends Thread {

    public MyThread() {
        System.out.println("构造方法打印:" + Thread.currentThread().getName());
    }

    @Override
    public void run() {
        System.out.println("run 方法打印:" + Thread.currentThread().getName());
    }
}

public class Test {

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

4. isAlive() 方法

isAlive() 方法判断当前线程是否处于活动状态

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println("run isAlive= " + this.isAlive());
    }
}

public class Test {

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        System.out.println("before isAlive=" + myThread.isAlive());
        myThread.start();
//        Thread.sleep(1000);
        System.out.println("after isAlive=" + myThread.isAlive());
    }
}

5. sleep() 方法

sleep() 方法在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。“正在执行的线程”是指 this.currentThread() 返回的线程

public class MyThread extends Thread {

    @Override
    public void run() {
        try {
            System.out.println("run begin threadName=" + this.getName() + " time=" + System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("run end threadName=" + this.getName() + " time=" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Test {

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        System.out.println("begin time=" + System.currentTimeMillis());
        myThread.start();
        System.out.println("end time=" + System.currentTimeMillis());
    }
}

6. getId() 方法

getId() 方法可以取得线程的唯一标识

public class Test {

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName() + " " + Thread.currentThread().getId());
    }
}

7. 停止线程

7.1 interrupt() 方法

interrupt() 方法无法马上中断线程,仅仅是对线程打了一个中断的标记,不是真正的中断线程

public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0;i < 500000;i++) {
            System.out.println("i=" + i);
        }
    }
}

public class Test {

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(500);
        myThread.interrupt();
    }
}

7.2 判断线程是否中断状态

  • interrupted() 方法判断当前线程是否是中断状态,如果是则清除中断状态

    public class Test {
    
        public static void main(String[] args) {
            Thread.currentThread().interrupt();
            System.out.println("是否停止1 " + Thread.interrupted());
            System.out.println("是否停止2 " + Thread.interrupted());
            System.out.println("end");
        }
    }
    
  • isInterrupted() 方法判断指定线程是否是中断状态,不清除中断状态

    public class MyThread extends Thread {
    
        @Override
        public void run() {
            for (int i = 0;i < 500000;i++) {
                System.out.println("i=" + i);
            }
        }
    }
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            MyThread myThread = new MyThread();
            myThread.start();
            Thread.sleep(500);
            myThread.interrupt();
            System.out.println("是否停止1 " + myThread.isInterrupted());
            System.out.println("是否停止2 " + myThread.isInterrupted());
            System.out.println("end");
        }
    }
    

7.3 停止线程-异常法(建议)

public class MyThread extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0;i < 500000;i++) {
                if (this.isInterrupted()) {
                    System.out.println("中断状态,退出");
                    throw new InterruptedException();
                }
                System.out.println("i=" + i);
            }
            System.out.println("for 循环下面");
        } catch (InterruptedException e) {
            System.out.println("MyThread catch");
            e.printStackTrace();
        }
    }
}

public class Test {

    public static void main(String[] args) {
        try {
            MyThread myThread = new MyThread();
            myThread.start();
            Thread.sleep(500);
            myThread.interrupt();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("main end");
    }
}

7.4 停止线程-沉睡法

  • 先休眠后中断

    public class MyThread extends Thread {
    
        @Override
        public void run() {
            try {
                System.out.println("run begin");
                Thread.sleep(20000);
                System.out.println("run end");
            } catch (InterruptedException e) {
                System.out.println("MyThread catch isInterrupted=" + this.isInterrupted());
                e.printStackTrace();
            }
        }
    }
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            MyThread myThread = new MyThread();
            myThread.start();
            Thread.sleep(2000);
            myThread.interrupt();
        }
    }
    
  • 先中断后休眠

    public class MyThread extends Thread {
    
        @Override
        public void run() {
            try {
                System.out.println("run begin");
                for (int i = 0;i < 500000;i++) {
                    System.out.println("i=" + i);
                    if (i == 400000) {
                        Thread.sleep(100);
                    }
                }
                System.out.println("run end");
            } catch (InterruptedException e) {
                System.out.println("MyThread catch");
                e.printStackTrace();
            }
        }
    }
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            MyThread myThread = new MyThread();
            myThread.start();
            myThread.interrupt();
        }
    }
    

7.5 停止线程-暴力法(不建议)

  • stop() 方法可以暴力停止线程

    public class MyThread extends Thread {
    
        @Override
        public void run() {
            try {
                int i = 0;
                while (true) {
                    System.out.println("i=" + (i++));
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            MyThread myThread = new MyThread();
            myThread.start();
            Thread.sleep(2000);
            myThread.stop();
        }
    }
    
  • stop() 方法已经被废弃。如果让线程强制停止可能会使一些清理性的工作得不到完成;还可能对锁对象进行了“解锁”,导致数据得不到同步处理,出现数据不一致问题

    public class Service {
    
        private String username = "a";
    
        private String password = "aaa";
    
        public String getUsername() {
            return username;
        }
    
        public String getPassword() {
            return password;
        }
    
        synchronized public void login(String username, String password) {
            try {
                 this.username = username;
                 Thread.sleep(20000);
                 this.password = password;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public class MyThread extends Thread {
    
        private Service service;
    
        public MyThread(Service service) {
            this.service = service;
        }
    
        @Override
        public void run() {
            service.login("b", "bbb");
        }
    }
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            Service service = new Service();
            MyThread myThread = new MyThread(service);
    
            myThread.start();
            Thread.sleep(2000);
            myThread.stop();
    
            System.out.println("username=" + service.getUsername() + " password=" + service.getPassword());
        }
    }
    

7.6 停止线程-返回法

public class MyThread extends Thread {

    @Override
    public void run() {
        int i = 0;
        while (true) {
            if (this.isInterrupted()) {
                System.out.println("停止了,退出");
                return;
            }
            System.out.println("i=" + (i++));
        }
    }
}

public class Test {

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(3000);
        myThread.interrupt();
    }
}

8. 暂停线程(不建议)

8.1 suspend() 和 resume() 方法的使用

public class MyThread extends Thread {

    private long i;

    public long getI() {
        return i;
    }

    @Override
    public void run() {
        while (true) {
            i++;
        }
    }
}

public class Test {

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(3000);

        myThread.suspend();
        System.out.println("suspend 1-1 time=" + System.currentTimeMillis() + " i=" + myThread.getI());
        Thread.sleep(3000);
        System.out.println("suspend 1-2 time=" + System.currentTimeMillis() + " i=" + myThread.getI());

        myThread.resume();
        Thread.sleep(3000);

        myThread.suspend();
        System.out.println("suspend 2-1 time=" + System.currentTimeMillis() + " i=" + myThread.getI());
        Thread.sleep(3000);
        System.out.println("suspend 2-2 time=" + System.currentTimeMillis() + " i=" + myThread.getI());
    }
}

8.2 suspend() 和 resume() 方法的缺点-独占

  • case1:

    public class Service {
    
        synchronized public void testA() {
            System.out.println(Thread.currentThread().getName() + " begin");
            if (Thread.currentThread().getName().equals("a")) {
                System.out.println(Thread.currentThread().getName() + " suspend");
                Thread.currentThread().suspend();
            }
            System.out.println(Thread.currentThread().getName() + " end");
        }
    }
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            final Service service = new Service();
    
            Thread a = new Thread() {
                @Override
                public void run() {
                    service.testA();
                }
            };
            a.setName("a");
            a.start();
    
            Thread.sleep(1000);
    
            Thread b = new Thread() {
                @Override
                public void run() {
                    service.testA();
                }
            };
            b.setName("b");
            b.start();
        }
    }
    
  • case2:System.out.println() 方法是 synchronized 同步块打印字符串,suspend() 导致 synchronized 持有的锁对象无法释放,所以案例中"main end"无法打印

    public class MyThread extends Thread {
    
        @Override
        public void run() {
            int i = 0;
            while (true) {
                i++;
                System.out.println(i);
            }
        }
    }
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            MyThread myThread = new MyThread();
            myThread.start();
            Thread.sleep(3000);
            myThread.suspend();
            System.out.println("main end");
        }
    }
    

8.3 suspend() 和 resume() 方法的缺点-不同步

public class Service {

    private String username = "a";

    private String password = "aaa";

    public void setValue(String username, String password) {
        this.username = username;
        if (Thread.currentThread().getName().equals("a")) {
            System.out.println(Thread.currentThread().getName() + " suspend");
            Thread.currentThread().suspend();
        }
        this.password = password;
    }

    public void getValue() {
        System.out.println(username + " " + password);
    }
}

public class Test {

    public static void main(String[] args) throws InterruptedException {
        final Service service = new Service();

        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                service.setValue("b", "bbb");
            }
        });
        a.setName("a");
        a.start();

        Thread.sleep(2000);

        service.getValue();
    }
}

9. yield() 方法

yield() 方法是当前线程放弃 CPU 资源给其他线程使用,但是放弃时间不确定,可能放弃后有马上获得资源

public class Test {

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                long beginTime = System.currentTimeMillis();
                int count = 0;
                for (int i = 0;i < 5000000;i++) {
//                    Thread.yield();
                    count = count + (i + 1);
                }
                long endTime = System.currentTimeMillis();
                System.out.println("耗时:" + (endTime - beginTime) + "毫秒");
            }
        });

        thread.start();
    }
}

10. 线程的优先级

10.1 线程优先级的继承特性

线程的优先级具有继承性,比如 A 线程启动 B 线程,则 A 和 B 两个线程的优先级是一样的

public class ThreadA extends Thread {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " priority=" + this.getPriority());
        ThreadB b = new ThreadB();
        b.start();
    }
}

public class ThreadB extends Thread {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " priority=" + this.getPriority());
    }
}

public class Test {

    public static void main(String[] args) {
        ThreadA a = new ThreadA();
        a.setPriority(8);
        a.start();
    }
}

10.2 setPriority() 方法

setPriority() 方法可以设置线程的优先级(1-10)。高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完成

public class ThreadA extends Thread {

    @Override
    public void run() {
        for (int i = 0;i < 500000;i++) {
            System.out.println(Thread.currentThread().getName() + " *****");
        }
    }
}

public class ThreadB extends Thread {

    @Override
    public void run() {
        for (int i = 0;i < 500000;i++) {
            System.out.println(Thread.currentThread().getName() + " ------");
        }
    }
}

public class Test {

    public static void main(String[] args) {
        ThreadA a = new ThreadA();
        a.setName("ThreadA");
        a.setPriority(1);

        ThreadB b = new ThreadB();
        b.setName("ThreadB");
        b.setPriority(10);

        a.start();
        b.start();
    }
}

11. 守护线程

Java 线程有两种:用户线程和守护线程。当进程中不存在用户线程了,则守护线程自动销毁。典型的守护线程例子是垃圾回收线程

public class MyThread extends Thread {

    @Override
    public void run() {
        try {
            int i = 0;
            while (true) {
                System.out.println(Thread.currentThread().getName() + " i=" + (i++));
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Test {

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.setDaemon(true);
        myThread.start();
        Thread.sleep(2000);
        System.out.println("main end");
    }
}

学自《Java多线程编程核心技术》