java并发编程 认识线程(一)

摘要: 此篇文字从进程和线程、线程的状态(生命周期),java中创建线程的三种方式说起,总体介绍线程,让大家对线程有一个初步的认识。

一  进程与线程

    进程是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位,

   简单来说进程就是你能看到的应用软件,而线程就是执行应用软件的具体执行部分。

   区别 :

    线程不能脱离进程而单独存在,线程依赖于进程,进程应当包含至少一个线程.

   进程开销大,线程开销小。

  系统会为每一个进程分配内存,而线程的内存则由进程分配,如果超过了则会出现内存溢出。

二  线程的状态

  点击JDK的Thread 类,在枚举类 state中可以看到线程的这样几个状态 

  public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }
  •  线程新建状态

   即我们刚刚启动线程,线程在等待启动的状态.

  •   线程运行状态

   这个运行状态可以又分2个状态,即就绪和运行状态,就绪就是指 还未运行的线程或者阻塞的线程 马上可以运行,但是还没有运行的状态,运行状态就是指线程运行时的状态.

  •   线程的阻塞状态

    即线程受 synchronized 等方法的影响,运行的方法被锁住,等待其他锁释放后才能进入此方法中。

  •   线程等待状态

   即线程受到 wait ,notify 方法的影响,等待另外一个方法的通知,然后继续执行的状态。

  •   线程等待超时状态

   即线程收到sleep(xxx)等方法的影响,等待规定的时候,然后再次执行的状态.

  •   线程结束状态

  即线程执行完成,释放资源的状态。

各种状态转换如下图:

三  java中创建线程的方式

  •  继承Thread类创建线程,
  1.   继承Thread,并且重写run方法
  2.   创建Thread子类的实例,
  3.   调用线程对象的start()方法
public class ThreadDemo extends  Thread {
    @Override
    public void run() {
        for (int i=0;i<100;i++){
            System.out.println(getName()+" "+i);
        }
    }
    public static void main(String[] args) {

        new ThreadDemo().start();
    }
}

程序可以通过setName为线程设置名字,也可以通过getName方法返回指定线程的名字,在默认情况下,主线程的名字为main,用户启动的多个线程的名字依次为Thread-0,Thread-1,Thread-n等

  •  实现Runnable 接口创建线程类
  1. 定义Runnable接口的实现类,并且重写run方法,该run方法的方法体是该线程的执行体
  2. 初始化实现类,并且作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象.
public class RunnableDemo implements Runnable{

    public static void main(String[] args) {
        RunnableDemo demo = new RunnableDemo();
        new Thread(demo,"name").start();
    }

    @Override
    public void run() {
        for (int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}
  • 使用Callable和future创建对象
  1. 创建Callable接口的实现类,并且实现call方法,该call方法将作为线程执行体,且该call方法有返回值,再创建Callable实现类的实例
  2. 使用Future类来包装Callable对象,该Feture对象封装了该Callable对象的call方法的返回值
  3. 使用FutureTask对象作为Thread对象的target创建并启动新线程
  4. 调用FutureTask对象的get方法来获取子线程的执行结束的返回值
public class CallableDemo {

    public static void main(String[] args) {
        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>) ()->{
           int i = 0 ;
           for (;i<100;i++){
               System.out.println(Thread.currentThread().getName()+"循环变量的值:"+i);
            }
            return i;
        });
        new Thread(task, "name").start();
        try {
            System.out.println("子线程返回的值为"+task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

 四  线程的中断和复位

在很多的业务处理中,我们常常因为某些原因需要中断一些线程,那么我们如何优雅的中断线程呢?

在Thread中提供了stop方法,但是已经被打了删除线,demo.stop(),下面我们可以通过自己的方式优雅的关闭线程.

public class VoliDemo  extends Thread{

    @Override
    public void run() {
        int i= 0;
        while (!isStop){
            i++;
            System.out.println(i);
        }
        System.out.println(i);
    }

    private volatile static  boolean isStop = false;

    public static void main(String[] args) throws InterruptedException {


        VoliDemo demo = new VoliDemo();
        demo.start();
        demo.stop();

        TimeUnit.SECONDS.sleep(1);
        isStop = true;
        System.out.println("线程结束");
    }

}

上面这段代码,我们定义了一个可见的属性,通过控制这个属性的值,让这个线程中止。

那么,java中是否还有可以中断线程的方法呢?

  • 线程中断

java中可以通过interrupt 方法来实现线程的中断

public class InterruptedDemo {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            while (true){
                if (Thread.currentThread().isInterrupted()){ //默认是false,当线程中断的时候进入
                    System.out.println("before:"+Thread.currentThread().isInterrupted());
                    Thread.currentThread().interrupted();//线程复位,变为false
                    System.out.println("after:"+Thread.currentThread().isInterrupted());
                }
            }
        });
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();//线程中断,属性变成true
    }
}
interrupted 属性实际返回1/0,转换后返回的是 false/true,默认是false
跟踪thread.interrupt()线程中断方法可知道 调用private native void interrupt0()方法,再次跟踪源码可以看到
interrupt0调用 JVM_Interrupt 方法,然后再次调用Thread::interrupt(thr)方法,代码如下
void Thread::interrupt(Thread* thread) {
  trace("interrupt", thread);
  debug_only(check_for_dangling_thread_pointer(thread);)
  os::interrupt(thread);
}
方法到 os::interrupt 这个地方之后,就会根据不同操作系统,然后调用不同的方法,最终的目的都是将interrupted,设置成true,线程就会终止。
  • 线程的复位

上面实例通过interrupted方法进行线程复位,跟踪源码,interrupted 方法调用 private native boolean isInterrupted(boolean ClearInterrupted),ClearInterrupted 值为true,标示线程进行复位,

底层通过Thread::is_interrupted方法调用各个系统自己的方法,如linux,将线程的标示设置为false。
 if (interrupted && clear_interrupted) {
    osthread->set_interrupted(false);
    // consider thread->_SleepEvent->reset() ... optional optimization
  }
另外通过InterruptedException 对线程进行复位操作.
由于线程处于睡眠状态或者等待状态,当线程发起中断请求的时候,线程会抛出一个InterruptedException 异常,由人工选择中断或者继续执行。
public class InterruptedExceptionDemo {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    System.out.println("线程状态:"+Thread.currentThread().isInterrupted());
                    Thread.currentThread().interrupt();//手动结束,
                    System.out.println("线程状态:"+Thread.currentThread().isInterrupted());
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();//线程中断,属性变成true
    }
}
ps:线程的复位是为了让外部知道,线程已经收到请求,但是不会立刻停止线程,需要外部来根据具体业务来进行控制。

猜你喜欢

转载自www.cnblogs.com/speeding360/p/11007982.html
今日推荐