01.Thread类

[TOC]

Thread类和Runnable接口

创建线程的常规方法有两种,一种是实现Runnable接口一种是扩展Thread类;
Runnable是一个函数式接口,只有一个void run()方法;

Runnable接口

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

注意到一个有趣的现象,这里的方法定义成abstract了,一般要得到同样的结果,可以直接在接口里面声明“void run();”,编译器会把它转成public abstract ...()的形式

Thread类

Thread类是一个线程的抽象,包含了几个重要的属性:

  • 优先级
  • 目标任务(Runnable实例)
  • 是否是守护线程
  • 线程名(默认Thread-N)、ID(默认N)、group(默认currentThread.getGroup()线程组的作用是:可以批量管理线程或线程组对象,有效地对线程或线程组对象进行组织。)

大部分方法的实现都是native方法,主要方法有:

  • sleep()

sleep方法使当前线程休眠(阻塞)N毫秒,但是不会释放持有的锁,在休眠期间可能会被系统挂起

  • yield()

主动让出CPU,不会释放持有的锁资源,但是调度器可以忽略让出CPU请求,且即使同意,让出的时间也不确定,一般让出后高优先级的线程能执行,但是也不一定,也许让出后自己又再次获得了;

/**
* 测试是否会释放锁
*/
public class YieldTest {
    static class MyTask {
        void a() throws InterruptedException {
            System.out.println("method a run");
            synchronized (this) {
                System.out.println("a lock");
                Thread.yield();
                Thread.sleep(1000);
                System.out.println("a unlock");
            }
        }

         void b() throws InterruptedException {
            System.out.println("method b run");
            synchronized (this) {
                System.out.println("b lock");
                Thread.yield();
                Thread.sleep(700);
                System.out.println("b unlock");
            }
        }
    }

    public static void main(String[] args) {
        final MyTask task = new MyTask();
        new Thread(() -> {
            try {
                task.a();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "ThreadA").start();
        Thread tb = new Thread(() -> {
            try {
                task.b();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "ThreadB");
        tb.setPriority(9);
        tb.start();
    }
}
结果:
method a run
a lock
method b run
a unlock
b lock
b unlock
证明不会释放锁
  • wait()/notify()/notifyAll()

条件对象上的等待和唤醒,必须在同步块或者同步方法内部执行,且方法接受者必须和锁对象一致。 wait方法会释放当前持有的锁,notify不会。

public class WaitTest {
    static class MyTask {
        private  final Object lock = new Object();
        void a() throws InterruptedException  {
            System.out.println("method a run");
            synchronized (lock) {
                System.out.println("a lock");
                lock.wait();
                System.out.println("a unlock");
            }
        }

        void b()   {
            System.out.println("method b run");
            synchronized (lock) {
                System.out.println("b lock");
                lock.notify();
                System.out.println("b unlock");
                try {
                    System.out.println("b sleep");
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    System.out.println("sleep end");
                }
            }
        }
    }
    public static void main(String[] args) {
        final MyTask task = new MyTask();
        new Thread(() -> {
            try {
                task.a();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "ThreadA").start();
         new Thread(() -> {
            task.b();
        }, "ThreadB").start();
    }
}
结果:
method a run
a lock
method b run
b lock
b unlock
b sleep
sleep end
a unlock  证明notify没有释放锁

  • start()

启动一个线程,实际执行时间要由OS调度,启动后执行run方法,注意不能对一个线程执行多次start()方法,会抛出异常

  • join() /join(n mills)

t.join()当前线程阻塞直到线程t执行结束

public class JoinTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            System.out.println("start run");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("run end");
        });
        t.start();
//      t.join();
        t.join(1000);
        System.out.println("main end");
    }
}
  • interrupt()/isInterrupted()/interrupted()

interrupt()中断一个线程,实际上只是发出中断请求,并将线程的中断标志置true,并不是操作系统层面上中断一个线程,线程内部可以检测中断标志位然后对应做出反应;
isInterrupted()返回线程中断状态,interrupted()是静态方法,也是返回中断状态,区别和注意点在于这个方法会清除标志位,也就是将true置为false;

  • 废弃的方法:suspend()/resume()

分别为挂起、恢复一个线程,会造成数据不一致问题以及死锁问题,已经弃用。suspend和resume方法很容易引起死锁问题。如果挂起一个持有锁的线程,挂起并不会释放锁,如果一直没有被resume,那么这个锁就永远被这个挂起的线程独占,也就造成了其他线程的饥饿。或者如果调用resume的线程等待获取被挂起线程持有的锁,那就会造成死锁。

  • 废弃的方法: stop

stop方法会强制终止一个执行中的线程,会强制结束线程内的未结束的方法,包括run方法,这种强制杀死线程的方法无异于断电操作,对于需要回滚的操作破坏力极强。对于被终止的线程,会立即释放线程中持有的所有锁,因为锁已经释放,对数据的破坏会被其他尚未停止的线程观察到。
stop方法抛出一个ThreadDeath错误对象来杀死线程。

  • get/setContextClassLoader

用来打破类加载机制的双亲委派模型


线程状态

  • New

创建了一个Thread对象时,还没有start,这个时候就是new状态

  • Runnable

运行和就绪统称为可运行,这个状态在start之后

  • Blocked

等待获取一个内部的对象锁(而非java.util.concurrent库里面的锁)时,进入阻塞状态;

  • Waiting

线程等待另一个线程通知一个条件时,进入等待状态,wait或join方法的调用会进入等待状态,另外等待concurrent库的Lock或者Condition时,也会进入等待状态;

  • TimeWaiting

Thread.sleep,Object.wait/join/Lock.tryLock/Condition.await的计时版本;

  • Terminated

自然结束、异常结束

线程的创建和启动

Thread的基本创建方式:

// 未指定优先级默认为5,java线程优先级从1-10,10最高
@Test
    public void createThread() {
        // extend class Thread
        class MyThread extends Thread { // or implements Runnable

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

        }
        new MyThread().start();
        // 匿名内部类
        new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " run");
            }

        }.start();
        // 匿名内部类
        new Thread(new Runnable() {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " run");

            }
        }).start();
        // lambda
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " run");
        },"Thread-lambda").start();

    }

Runnable接口的run方法是没有返回值的(Thread类也是实现了Runnable接口),且不能抛出受检查的异常,如果需要线程执行结束的结果,需要将结果写进一个外部变量中。Callable接口可以有返回值且能抛出受检查的异常。Callable接口通常用在执行器中,返回一个Future对象可以阻塞或非阻塞的获取执行结果,在执行器框架中,Runnable接口和Callable接口都是任务的抽象,对应不同的使用场景。
如果要将Callable接口用于单个线程需要一个帮助类FutureTask,它将Callable转换成一个Runnable,使其可以传入Thread的构造器。
Callable也是一个函数式接口:


@FunctionalInterface
public interface Callable {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

示例:

        Callable c = () -> {
            System.out.println(Thread.currentThread().getName() + " run");
            return 100;
        };
        FutureTask task = new FutureTask<>(c);
        new Thread(task).start();
        try {
            //阻塞直到获取结果
            System.out.println(task.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

停止一个线程

停止一个线程不能使用stop方法,这个方法十分的危险!
常用的方法有轮询标志位或者利用InterruptException(抛出异常线程肯定结束)

public class StopAThread {
    @Test
    public void stopwithCheckInterruptStaus() throws InterruptedException {

        Thread t = new Thread(() -> {
            System.out.println("work0");
            System.out.println("work1");
            System.out.println("work2");
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("no,current is not interrupted");
            }
            System.out.println(Thread.currentThread().getName() + " will end");
        });
        t.start();
        Thread.sleep(1);
        t.interrupt();
        t.join();
        System.out.println("main end");
    }

    @Test
    public void stopByInterruptedException() throws InterruptedException {
        Thread t = new Thread(() -> {
            System.out.println("work0");
            System.out.println("work1");
            System.out.println("work2");
            try {
                TimeUnit.SECONDS.sleep(30);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " will end");
        });
        t.start();
        Thread.sleep(1);
        t.interrupt();
        t.join();
        System.out.println("main end");
    }

    @Test
    public void stopByCheckFlag() throws InterruptedException {
        final boolean[] flag = {false}; // 注意变量的可见性,此处由于是局部变量,是肯定可见的
        Thread t = new Thread(() -> {
            System.out.println("work0");
            System.out.println("work1");
            System.out.println("work2");
            while (!flag[0]) {
                System.out.println("working");
            }

            System.out.println(Thread.currentThread().getName() + " will end");
        });
        t.start();
        Thread.sleep(1);
        flag[0]=!flag[0];
        t.join();
        System.out.println("main end");
    }
}

你可能感兴趣的:(01.Thread类)