Java多线程之一

进程与线程

进程

进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位,好比咱们windows电脑上运行的一个程序就是一个进程。在传统进程中进程是资源分配和调度的一个基本单位,在后来引入线程概念后,进程就变成了资源分配的基本单位但不是调度的基本单位。windows

为何要有线程

在说线程前,总结下进程的特色:api

  1. 进程是一个可拥有资源的独立单位;
  2. 进程是一个可独立调度和分派的基本单位。

这样来看的话好像是没什么问题,可是在多任务环境中,不可能说让全部任务排队,前面的处理完了才处理后面的任务。若是要让用户==感受==到任务都是一块儿执行的,那么就必须在进程之间频繁切换。问题在于若是要进行进程的切换须要作不少的工做,必需要保存好当前CPU的上下文,好让CPU下次被分配到当前进程时能够继续往前执行,而后还须要设置新的进程的CPU上下文,在这个过程当中会花费不少时间。因为这个缘由就限制了系统中进程数目不能多。安全

为了解决这个限制,后来提出将进程的两个属性分开,由操做系统分开处理,即对于做为调度和分派的基本单位,但不一样时做为拥有资源的单位;而对于拥有资源的基本单位,又不对其进行频繁的切换。正是在这种思想的指导下,造成了线程的概念。bash

线程

在多线程操做系统中中,一般是在一个进程中包括多个线程,每一个线程都是独立调度和分派的基本单位。资源由进程来拥有,线程不拥有资源。同一个进程之间的线程切换不会致使进程的切换,只有不一样进程间的线程切换才会致使进程切换。并且线程的切换则仅需保存和设置少许寄存器内容,不会同进程切换需求建立和销毁进程控制块等,因此很是迅速,因此其十分适合高并发环境。服务器

线程的状态(Java)

public enum State {
        NEW,//新建 线程被建立,可是没有调用start方法

        RUNNABLE,//可运行 表示当前线程能够运行,但实际是否运行有cpu决定

        BLOCKED,//阻塞 其余线程得到锁,当前线程被阻塞在得到锁处

        WAITING,//等待 等待其余条件成熟进入可运行状态

        TIMED_WAITING,//计时等待 在一个指定时间内等待,超时后放弃

        TERMINATED;//终止 线程执行完毕
    }
复制代码

线程的建立方式

Thread

继承Thread类:多线程

class TestThread extends Thread{
        @Override
        public void run() {
            super.run();
            //do working
        }
    }
复制代码
Runnable

实现Runnable接口:并发

static class TestRunnale implements Runnable{

        @Override
        public void run() {
            //do working
        }
    }

    public static void main(String[] args) {
        TestRunnale runnale = new TestRunnale();
        Thread thread = new Thread(runnale);
        thread.start();
    }
复制代码

线程的中断

不安全的中断

Thread的api中提供了一些终止线程的方法,好比stop()suspend(),resume(),可是这些方法目前在JDK中已经被标记位过期,由于这些方法具备死锁倾向,已经被明确表示不支持使用。socket

中断线程API

interrupt() 中断线程,本质是将线程的中断标志位设为true,其余线程向须要中断的线程打个招呼。是否真正进行中断由线程本身决定。ide

isInterrupted() 线程检查本身的中断标志位高并发

静态方法Thread.interrupted() 将中断标志位复位为false

中断标志位

自定义一个Boolean类型的中断标志位,提供一个中断方法,线程一直循环检测该标志位,标志位被设置为退出状态是终止线程。

public class FlagCancel {
    static class Flag extends Thread{
        //中断标志
        public static boolean flag = false;

        @Override
        public void run() {
            int i = 0;
            while(!flag){
                System.out.println(i++);
                if(i>=3){
                    try {
                        Thread.sleep(200);
                        //interrupt();
                        if(i == 10)
                            cancel();//修改中断状态,退出线程
                        System.out.println("thread:" + isInterrupted());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("cancel...");
                }
            }
        }

        public static void cancel(){
            flag = true;
        }
    }

    public static void main(String[] args) {
        Flag test = new Flag();
        test.start();
        test.setPriority(10);//这里的设置优先级其实没什么用。cpu不会理你的。。。
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main:" + test.isInterrupted());//这里属于主线程(main)
    }
}
复制代码

正常来讲上面的形式没有什么问题,咱们写代码的时候,提供一个修改中断为状态的方法,并根据咱们本身的业务逻辑来定义何时中断,可是若是咱们手动设置中断就有问题了,将上面代码中注释的interrupt();打开。interrupt()方法是用来中断线程的,可是在上面的逻辑中即便调用了该方法也不会当即中断,而必需要等待中断为被修改后才能退出。

安全的中断

上面介绍了中断相关的api和使用中断标志位来中断线程,可是中断标记位没法捕获异常状况。可是isInterrupted()方法会一直检查线程的中断状态,因此咱们能够用这个方法来实现安全的中断。

public class SafeInterrupt extends Thread {

    private boolean flag = false;

    @Override
    public void run() {
        int i = 0;
        System.out.println(Thread.currentThread().getName() + ":" +Thread.currentThread().isInterrupted());
        while (!flag && !Thread.currentThread().isInterrupted()) {
            System.out.println(i++);
            try {
                synchronized (this) {
                    if (i > 3) {
                        //Thread.sleep(1000 * 60 * 60 * 24);
                        wait();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 这里必须将须要中断的线程做为参数传过来
     * 用以进行中断
     * @param t(Thread)
     */
    public void cancel(Thread t) {
        System.out.println("ready stop currentThread...");
        flag = true;
        //将须要中断的线程的中断标志位设置为true
        t.interrupt();
        System.out.println(t.getName() + ":" + t.isInterrupted());
    }

    public static void main(String[] args) throws InterruptedException {
        SafeInterrupt safeInterrupt = new SafeInterrupt();
        safeInterrupt.start();
        Thread.sleep(100);
        safeInterrupt.cancel(safeInterrupt);
    }
}
复制代码
不可中断的状况

好了,到如今咱们已经能够安全的处理线程的中断了,可是还没完,由于不是全部的线程都是会响应中断的。好比IO的read()/write() 等就不会响应中断。而若是咱们想不让其继续阻塞的话就须要咱们手动的关闭底层的套接字。

public class CloseSocket extends Thread {
    private Socket socket;
    private InputStream in;

    public CloseSocket(Socket socket, InputStream in) {
        this.socket = socket;
        this.in = in;
    }

    //重写中断方法 在中断线程时中断套接字
    @Override
    public void interrupt() {
        try {
            //关闭底层套接字
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //中断线程
            super.interrupt();
        }

    }
}
复制代码

还有想死锁之类的不响应中断的状况用代码已经基本解决不了了,只能检查代码修改重启服务器啦。

相关文章
相关标签/搜索