Java 线程的基本API

线程的基本操作

进行Java 并发设计的第一步要了解Java中为线程提供的API。比如如何新建、启动、终止、中断、等待、通知等。

首先了解一下一个线程的几种状态

线程状态

在这里插入图片描述

线程状态在Java中是通过一个Thread的内部枚举State标识的。

NEW---->RUNNABLE---->BLOCKED---->WAITING---->TIMED_WAITING---->TERMINATED

1、创建状态(Thread.State.NEW)

如果只是创建了一个线程,而没有启动它(start)则线程状态为创建状态。

例如:Thread thread1 = new Thread();

处于创建状态的线程还没有获得应有的资源,所以,这是一个空的线程。线程只有通过启动后,系统才会为它分配资源。

2、可运行状态(Thread.State.RUNNABLE)

通过调用t.start()启动一个线程,使该线程进入可运行(Thread.State.RUNNABLE)的状态。处于该状态的线程进入就绪队列中,当线程获取CPU的时间片则开始运行。

3、阻塞状态(Thread.State.BLOCKED)

受阻塞并且正在等待监视器锁的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 之后再次进入同步的块/方法。

4、等待状态(Thread.State.WAITING、TIMED_WAITING)

某一等待线程的线程状态。某一线程因为调用下列方法之一而处于等待状态:

不带超时值的 Object.wait

不带超时值的 Thread.join

LockSupport.park

处于等待状态的线程正等待另一个线程,以执行特定操作。 例如,已经在某一对象上调用了 Object.wait() 的线程正等待另一个线程,以便在该对象上调用 Object.notify() 或 Object.notifyAll()。已经调用了 Thread.join() 的线程正在等待指定线程终止。

TIMED_WAITING具有指定等待时间的某一等待线程的线程状态。某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:

Thread.sleep

带有超时值的 Object.wait

带有超时值的 Thread.join

LockSupport.parkNanos

LockSupport.parkUntil

5、结束状态(Thread.State.TERMINATED)

线程的基本操作
1. 新建线程

新建一个线程有三种方式

  • 继承Thread类,重写run方法,Thread实现了Runnable接口,本身就是一个执行任务,Thread的run方法中调用的就是Runnable的run方法
  • 实现Runnable接口,new Thread实例时传入Runnable的实例
  • 通过线程池创建

/**
 * 线程初始化过程:
 * 1. new Thread()过程中会为线程设置线程组、执行目标、线程名、线程栈大小
 *    默认和主线程一个组;Thread 实现了Runnable,本身即为一个执行目标
 * 2. 真正创建线程是在start()中,这里使用了本地调用,通过C代码初始化线程需要的系统资源。
 *    此时start()的这个线程处于就绪状态,当得到CPU的时间片后就会执行其中的run()方法。
 * 3. 线程从创建到最终的消亡,要经历若干个状态。
 *    一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、等待(time waiting、waiting)、消亡(dead)。
 *
 */
public class ThreadNewWays {

    public static void main(String[] args){
        firstWay();
        secondWay();
        thirdWay();
    }

    /**
     * 继承Thead类重写run方法, Thread实现了Runnable接口,本身就是一个执行任务
     */
    public static void firstWay() {
        Thread thread = new Thread(){
            @Override
            public void run() {
                System.out.println("this is first way to new thread!");
            }
        };
        thread.start();
    }

    /**
     * 通过实现Runnable接口告诉线程执行什么任务
     */
    public static void secondWay() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("this is second way to new thread!");
            }
        }).start();
    }

    /**
     * 通过线程池创建线程
     */
    public static void thirdWay() {
        ExecutorService service = new ThreadPoolExecutor(1, 1, 0L,
                TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        service.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("this si third way to new thread !");
            }
        });

        service.shutdown();
    }
}
2. 线程终止

一般来说,线程在执行完之后会自动关闭,可是一些服务端的线程可能需要手动关闭。Thread中的stop方法已经被弃用,stop方法关闭线程太过暴力,强行把执行到一半的线程关闭可能导致数据不一致的问题。可通过一个volatile变量来达到关闭线程的目的。

public class ThreadStop {

    public static User user = new User();

    public static void main(String[] args){
        Thread thread1 = new Thread(new ReadObjectThread());
        thread1.start();
        while (true) {
            ChangeObjectThread thread2 = new ChangeObjectThread();
            thread2.start();

            try {
                Thread.sleep(110);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            /**
             * stop 过于暴力,会破坏数据
             * 可通过一个volatile变量来达到关闭线程的目的
             */
//            thread2.stop();
            thread2.stopMe();
        }

    }

    public static class ChangeObjectThread extends Thread {

        volatile boolean stopMe = false;

        public void stopMe(){
            this.stopMe = true;
        }

        @Override
        public void run() {
            while (true) {
                if (stopMe) {
                    System.out.println("stop thread");
                    break;
                }

                synchronized (user) {
                    int v = (int) (System.currentTimeMillis()/1000);
                    user.setId(v);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    user.setName(String.valueOf(v));
                }
                Thread.yield();
            }
        }
    }

    public static class ReadObjectThread implements Runnable{

        @Override
        public void run() {
            while (true) {
                synchronized (user) {
                    if (user.getId() != Integer.valueOf(user.getName())) {
                        System.out.println(user);
                    }
                }
                Thread.yield();
            }
        }
    }

    public static class User{
        private int id;
        private String name;

        public User() {
            this.id = 0;
            this.name = "0";
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
}
3. 线程中断

Java中,线程中断是一种重要的线程间协作机制。线程中断并不会立即停止,而是给线程发一个通知告知目标线程应该退出,至于目标线程接到通知之后如何处理,则完全由目标线程决定。所以一般目标线程中会去处理中断退出。

public class ThreadInterrupt {

    public static void main(String[] args) throws Exception{
//        noDealInterrupt();
//        dealInterrupt();
        dealInterruptException();
    }

    /**
     * 中断异常处理
     * @throws Exception
     */
    public static void dealInterruptException () throws Exception {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("Thread is interrupt");
                        break;
                    }

                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        System.out.println("Interrupt when sleep");
                        //Thread.sleep()方法由于中断而抛出异常,此时,他会清除中断标记,所以在异常处理中,再次设置中断标记。
                        Thread.currentThread().interrupt();
                    }

                    Thread.yield();
                }
            }
        });
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    }

    /**
     * 处理中断
     * @throws Exception
     */
    public static void dealInterrupt() throws  Exception {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("thread is interrupt");
                        break;
                    }
                    Thread.yield();
                }
            }
        });

        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    }

    /**
     * 没有处理中断
     * @throws Exception
     */
    public static void noDealInterrupt() throws Exception{
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Thread.yield();
                }
            }
        });

        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    }
}

4. 等待(wait)和通知(notify)
  • 为了支持多线程间的协作,JDK提供了等待(wait)和通知(notify)方法,这两个方法属于Object中的方法,所有对象都可以调用这两个方法。
  • 当一个线程在一个对象实例上调用了wait()方法后,当前线程就会在这个对象上等待,线程会一直等待到其他线程调用了notify()方法。
注意:
  1. 无论是wait还是notify必须包含在对应的synchronized语句中,首先获取到目标对象的一个监视器。如果一个线程调用了object.wait(),那么它就会进入object对象的等待队列。
  2. Object.wait()和Thread.sleep()都可以让线程等待若干时间,wait可以被唤醒和释放目标对象的锁,Thread.sleep()不会释放任何资源。
public class ThreadWaitNotify {

    private static Object obj = new Object();

    public static void main(String[] args){
        Thread t1 = new T1();
        Thread t2 = new T2();
        t1.start();
        t2.start();
    }

    public static class T2 extends Thread {

        public void run() {
            synchronized (obj) {
                System.out.println("T2 start " + System.currentTimeMillis());
                obj.notify();

                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("T2 end " + System.currentTimeMillis());
            }
        }
    }


    public static class T1 extends Thread {

        public void run() {
            synchronized (obj) {
                System.out.println("T1 start " + System.currentTimeMillis());
                try {
                    System.out.println("T1 wait for Object");
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("T1 end " + System.currentTimeMillis());
            }
        }
    }

}
5 . 等待线程结束(Join)和谦让(yield)
  • 一个线程依赖另一个线程,使用join方法依赖一个线程进来,当前线程必须等待依赖线程执行完毕,才能继续执行。
  • 静态方法yield方法,让当前线程让出CPU,但是还会参与CPU资源的争夺。
public class ThreadJoinYield {
    public volatile  static int i = 0;
    public static void main(String[] args) throws Exception{
        Thread thread = new AddThread();
        thread.start();
        //阻塞当前线程,即main线程,直到AddThread线程执行完毕
        thread.join();
        System.out.println(i);

    }

    public static class AddThread extends Thread {
        public void run() {
            System.out.println("AddThread started ...");
            for (i=0;i<1000000;i++) {
                if (i%50000==0) {
                    System.out.println("thread yield cpu");
                    //使当前线程让出CPU时间
                    Thread.yield();
                }
            }
        }
    }
}
6. 线程组

先建线程的时候可以指定线程组,把功能相同的线程放置在一个线程组里。
public Thread(ThreadGroup group, Runnable target, String name)

7. 守护线程(Daemon)

thread.setDaemon(true);

注意:
一个Java应用只有守护线程时,Java虚拟机就会自然退出。
设置守护线程必须在start()方法之前设置

8. 线程优先级

优先级高的线程在竞争资源时会更有优势,更可能抢占资源,这也只是一个概率事件。

Java中使用1到10表示线程的优先级,数字越大优先级越高。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值