[多线程]Java多线程02_线程操作

1. 线程常用方法

1)currentThread()

获取当前线程的对象信息,获取该对象就可以获取该线程的信息。常用方法是getName(),可以获取当前线程的名字。

  • 编写线程类
public class CurrentDemo implements Runnable {

    public CurrentDemo() {
        System.out.println("构造方法被调用,当前线程:" + Thread.currentThread().getName());
    }


    @Override

    public void run() {
        System.out.println("Run方法被调用,当前线程:" + Thread.currentThread().getName());
    }
}
  • 启动
public static void startVurrentDemo() {
    /** 由main调用构造方法 */
    CurrentDemo demo = new CurrentDemo();

    /** 由线程1调用run方法 */
    Thread t1 = new Thread(demo, "线程1");
    t1.start();
}
  • 结果
构造方法被调用,当前线程:main
Run方法被调用,当前线程:线程1

由结果可知,调用构造方法的也是一个线程,而且是main线程,而调用run方法的就是自己创建出来的一个线程了。

2)isAlive()

方法作用:用于测试线程是否处于活动状态。
活动状态:线程已经启动(调用start()方法),尚未结束。线程处于正在运行或者准备开始运行状态,则认为线程是“存活”的。

  • 线程类:
public class AliveDemo implements Runnable {

    public AliveDemo() {
        System.out.println("构造方法被调用");
        System.out.println("当前线程是:" + Thread.currentThread().getName());

        /** 注意调用当前的线程是Main,所以调用isAlive的时候返回的是true */
        System.out.println("isAlive: " + Thread.currentThread().isAlive());
        System.out.println("构造方法END");
    }

    @Override
    public void run() {
        System.out.println("RUN方法被调用");
        System.out.println("当前线程是:" + Thread.currentThread().getName());
        System.out.println("isAlive: " + Thread.currentThread().isAlive());
        System.out.println("RUN方法END");
    }
}
  • 启动类
public static void startIsAlive() {
    Thread t1 = new Thread(new AliveDemo(), "线程1");
    t1.start();
    System.out.println("Main: isAlive: " + t1.isAlive());
}
  • 结果
构造方法被调用
当前线程是:main
isAlive: true
构造方法END
RUN方法被调用
当前线程是:线程1
isAlive: true
RUN方法END
Main: isAlive: true

注意:在调用start的后面加上当前线程睡眠1秒钟,那t1输出的结果就不同了(变为false),因为在这个例子中,调用isAlive()的时候刚好线程还没运行结束

3)sleep()

方法作用:让当前线程沉睡指定的毫秒数。

  • 线程类:
public class SleepDemo implements Runnable {


    @Override
    public void run() {
        try {
            System.out.println("RUN开始,当前时间:" + System.currentTimeMillis());
            Thread.sleep(1000L);
            System.out.println("RUN结束,当前时间:" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 启动类
public static void startSleep() {
    Thread t1 = new Thread(new SleepDemo(), "睡眠线程");
    t1.start();
}
  • 结果
RUN开始,当前时间:1512970671432
RUN结束,当前时间:1512970672436

4)getId()

方法作用:获取线程唯一id值

2. 操作线程方法

1)停止线程

interrupt(): 给线程打上中断标记
interrupted(): 测试当前线程是否已经中断,执行后将状态标志设置为false(调用该方法后再调用第二次会返回false)
isInrrupted(): 测试线程Thread对象是否已经是中断状态,但不清除标志。

① interrupt()示例

  • 线程类
public class Stop01Demo implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 500000; i++) {
            System.out.println(i);
        }
    }
}
  • 启动类
public static void startStop01() {
    Thread t1 = new Thread(new Stop01Demo(), "测试停止标记");
    t1.start();
    try {
        Thread.sleep(200L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    t1.interrupt();
}
  • 结果
...
499994
499995
499996
499997
499998
499999

根据结果来看,调用interrupt()并没有停止线程,而是为线程做了标记,配合interrupted()(1. 测试当前线程; 2. 有清除功能)和isInrrupted()(无清除功能)在run方法内部来做线程的退出。

② interrupted()示例

  • 线程类和启动类合一
public static void startInterrupted() {
    Thread.currentThread().interrupt();
    System.out.println("是否停止1:" + Thread.interrupted());
    System.out.println("是否停止2:" + Thread.interrupted());// 已经清除标记,所以返回 false

    /** 重新打上停止的标记 */
    Thread.currentThread().interrupt();
    System.out.println("是否停止3:" + Thread.interrupted());
}
  • 结果
是否停止1:true
是否停止2:false
是否停止3:true

③ isInterrupted()示例

  • 线程类
public class Stop02Demo implements Runnable {
    @Override
    public void run() {
        while (true) {
        }
    }
}
  • 启动类
public static void startStop02() {
    Thread t1 = new Thread(new Stop02Demo());
    t1.start();
    t1.interrupt();
    System.out.println("是否停止:" + t1.isInterrupted());
    System.out.println("是否停止:" + t1.isInterrupted());
}
  • 结果
是否停止:true
是否停止:true

那么结合isInterrupted()这个方法,我们可以在我们的逻辑里面做相关的退出操作:

  • 线程类:
public class Stop03Demo implements Runnable {
    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("END!");
                return;
            }
        }
    }
}

④ 异常退出法

我们知道当线程发生异常的时候,如果没有使用try-catch进行捕获处理的话,那么线程是会退出运行的。那么只需要把上面的return改成抛出一个运行时异常即可,但是不知道为啥,我始终认为这个方法不够优雅。

⑤ sleep()和interrupt()

  • sleep()interrupt()
  • interrupt()sleep()
1. 先sleep()interrupt()
  • 线程类
public class SleepAndInterruptDemo implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            System.out.println("InterruptedException!");
            e.printStackTrace();
        }
    }
}
  • 启动类
public static void startSleepAndInterrupt() {
    Thread t1 = new Thread(new SleepAndInterruptDemo());
    t1.start();
    t1.interrupt();
}
  • 结果
InterruptedException!
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at p02.threadClz.SleepAndInterruptDemo.run(SleepAndInterruptDemo.java:7)
    at java.lang.Thread.run(Thread.java:748)

结果刚好就是sleep方法需要捕获的异常

2. 先interrupt()sleep()
  • 线程类
public class InterruptAndSleepDemo implements Runnable {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 100000; i++) {
                /** 拖延时间 */
                System.out.println(i);
            }
            System.out.println("Sleep");
            Thread.sleep(200000L);
            System.out.println("Sleep End");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 启动类
public static void startInterruptAndSleep() {
    Thread t1 = new Thread(new InterruptAndSleepDemo());
    t1.start();
    t1.interrupt();
}
  • 结果
...
99998
99999
Sleep
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at p02.threadClz.InterruptAndSleepDemo.run(InterruptAndSleepDemo.java:11)
    at java.lang.Thread.run(Thread.java:748)

就是睡不了

⑥ stop()示例

stop方法是一个已经被官方定义为过时的方法,因为这个方法是有缺陷的,它导致线程的后需清理工作做的不到位。所以在生产环境中禁止去使用stop方法进行线程的停止。

2)暂停线程

① 对应的方法有suspend()resume()

  • 线程类
public class SuspendDemo implements Runnable {
    public int i = 0;
    @Override
    public void run() {
        while (true) {
            i++;
        }
    }
}
  • 启动类
public static void startSuspendDemo() throws InterruptedException {
    SuspendDemo demo = new SuspendDemo();
    Thread t1 = new Thread(demo, "Suspend Thread");
    t1.start();

    /** 暂停运行 */
    t1.suspend();
    System.out.println("当前时间:" + System.currentTimeMillis() + ", i = " + demo.i);
    Thread.sleep(5000);
    System.out.println("当前时间:" + System.currentTimeMillis() + ", i = " + demo.i);

    /** 恢复运行 */
    t1.resume();
    Thread.sleep(5000);

    /** 再次暂停 */
    t1.suspend();
    System.out.println("当前时间:" + System.currentTimeMillis() + ", i = " + demo.i);
    Thread.sleep(5000);
    System.out.println("当前时间:" + System.currentTimeMillis() + ", i = " + demo.i);
}
  • 结果
当前时间:1512978809877, i = 205824
当前时间:1512978814881, i = 205824
当前时间:1512978819884, i = -1595043396
当前时间:1512978824887, i = -1595043396

② 占用同步资源

  • 资源类
public class SynchronizedObject {

    public synchronized void doSomething() {
        System.out.println("begin doSomething");
        if (Thread.currentThread().getName().equals("Thread01")) {
            System.out.println("暂停当前线程");
            Thread.currentThread().suspend();
        }
        System.out.println("END");
    }

}
  • 线程类
public class SuspendDemo2 implements Runnable {

    private SynchronizedObject synchronizedObject;

    public SuspendDemo2(SynchronizedObject synchronizedObject) {
        this.synchronizedObject = synchronizedObject;
    }

    @Override
    public void run() {
        synchronizedObject.doSomething();
    }
}
  • 运行类
public static void startSuspendDemo2() {
    SynchronizedObject synchronizedObject = new SynchronizedObject();
    Thread t1 = new Thread(new SuspendDemo2(synchronizedObject), "Thread01");
    Thread t2 = new Thread(new SuspendDemo2(synchronizedObject), "Thread02");

    t1.start();
    t2.start();

}
  • 结果
begin doSomething
暂停当前线程

可以看到t1线程在执行资源占用锁的时候,停止了,但是并没有把锁释放出来让其他线程用。

③ 另外,suspendresume会导致数据不一致

这个问题,跟资源有没有加锁是有关系的,但是加锁了会出现上面第②独占锁的情况,不加的话,资源不安全也是正常的,所以,不使用suspendresume才是正确的选择,后期会说到synchronize锁的问题,可以解决暂停以及重新恢复的问题。

3)让出资源

yield(): 让出当前CPU资源,但是可能刚刚让出资源,就又被该线程抢到了。

  • 线程类
public class YieldDemo implements Runnable {

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i < 50000000; i++) {
            /** 这里指定一个线程名字用来让出CPU */
            if (Thread.currentThread().getName().equals("YieldThread")) {
                Thread.yield();
            }
            count += i;
        }
        System.out.println(Thread.currentThread().getName() + "耗时:" + (System.currentTimeMillis() - start));
    }

}
  • 启动类
public static void startYield() {
    YieldDemo demo = new YieldDemo();
    Thread t1 = new Thread(demo, "YieldThread");
    Thread t2 = new Thread(demo, "YieldThread2");
    t1.start();
    t2.start();
}
  • 结果
YieldThread2耗时:120
YieldThread耗时:11132

可以看出来,让出CPU的线程执行的非常慢。

4)优先级

通过线程对象调用setPriority(int newPriority)来设置线程的优先级。优先级的范围是1-10,如果不是就会抛出异常。

需要注意:并不是优先级高的任务会先执行完,而是说优先级高的线程会更有机会获得CPU时间片,从而更快的执行相对应的任务。

特性:
– 继承
– 规则性
– 随机性

通过优先级来模拟龟兔赛跑

  • 动物类
public class Animal implements Runnable {
    @Override
    public void run() {
        for (int j = 0; j < 1000000; j++) {
            System.out.println(Thread.currentThread().getName());
        }
        System.out.println(Thread.currentThread().getName() + "跑完了100米");
    }
}
  • 线程
public static void rabbitAndTurtle() {
    Thread t1 = new Thread(new Animal(), "兔子");
    Thread t2 = new Thread(new Animal(), "乌龟");

    t1.setPriority(Thread.MAX_PRIORITY);// 兔子比较快
    t2.setPriority(Thread.MIN_PRIORITY);// 乌龟比较慢

    t2.start();
    t1.start();
}
  • 结果

结果太长了,但是每次运行“均”都是龟比较慢跑完

3. 守护线程

守护线程:当主线程退出运行的时候,启动的线程也就退出运行。如:GC(垃圾回收器),当Java线程退出的时候,它的存在也就失去了意义,所以他也退出了运行。默认是关闭的。

  • 线程类
public class DaemonDemo implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                System.out.println("当前时间是: " + LocalDate.now());
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 启动类
public static void startDaemon() {
    Thread t1 = new Thread(new DaemonDemo(), "守护线程");
    t1.setDaemon(true);
    t1.start();
    try {
        Thread.sleep(5000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
  • 结果
当前时间是: 2017-12-11
当前时间是: 2017-12-11
当前时间是: 2017-12-11
当前时间是: 2017-12-11
当前时间是: 2017-12-11

当设置了t1.setDaemon(true);的时候,主线程退出运行,则其启动的线程也退出了运行。