java关于并发的总结之三

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38095094/article/details/79249482

线程简介

线程的理解

现代操作系统调度的最小单元是线程,也加轻量级进程,在一个进程里可以创建多个线程,这些线程拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。

使用多线程的原因

  • 更多的处理器核心
  • 更快的响应时间
  • 更好的变成模型

线程优先级

现代操作系统基本采用时分的形式调度运行的线程。在java线程中,通过一个整型成员变量priority来控制优先级,对应的方法为 setPriority() ,默认优先级是5,优先级高的线程分配时间片的数量要多余优先级低的线程。

线程的状态

状态名称 说明
NEW 初始状态,线程被构建,但还没有调用start()方法
RUNNABLE 运行状态,java线程将操作系统中的就绪和运行两个状态统称为“运行中”
BLOCKED 阻塞状态,表示线程阻塞于锁
WAITING 等待状态,只有当其它线程进行通知或者中断的时候,等待状态的线程才会从wait()中返回
TIME_WAITING 超时等待状态,在指定时间内可以自行返回
TERMINATED 终止状态,表示当前线程已经执行完毕

Daemon线程

Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。当一个java虚拟机中不存在非Daemon线程的时候,java虚拟机将会退出。一个线程如果要设置为Daemon线程,必须要在这个线程start()之前进行设置。

public class Daemon {
    public static void mian(String[] args){
        Thread thread = new Thread(new DaemonRunner(),"DaemonRunner");
        thread.setDaemon(true);     //设置为Daemon线程
        thread.start();
    }

    static class DaemonRunner implements Runnable{
        public void run(){
            try{
                Thread.sleeep(4000);
            }finally{
                System.out.println("Hello world");
            }
        }
    }
}

运行程序之后,发现并没有输出。这是因为main线程和Daemon线程都在运行,main线程结束之后,Daemon就算没有运行结束也会退出。

启动和终止线程

构造线程

线程对象在构造时需要提供线程需要的属性,如线程所属的线程组、线程优先级、是否是Daemon线程等信息。

启动线程

线程对象在初始化完成之后,调用start()方法可以启用这个线程。

线程中断

其它线程通过调用该线程的interrupt()方法对该线程进行中断操作,线程还可以通过isInterrupted()方法来判断该线程是否被中断。
注意:在抛出InterruptedException的方法(比如Thread.sleep())之前,java虚拟机会先将该线程的中断标识清除,然后抛出InterruptedException,此时调用isInterrupted()方法将会返回false。

public class Interrupted{
    static class SleepRunner implements Runnable{
        public void run(){
            while(true){
                Thread.sleep(1000);
            }
        }
    }
    static class BusyRunner implements Runnable{
        public void run(){
            while(true){}
        }
    }

    public static void main(String args[]){
        Thread sleepThread = new Thread(new SleepRunner(),"sleepThread");
        sleepThread.setDaemon(true);
        Thread busyThread = new Thread(new BusyRunner(),"busyThread");
        busyThread.setDaemon(true);
        sleepThread.start();
        busyThread.start();
        Thread.sleep(5000);
        sleepThread.interrupt();
        busyThread.interrupt();
        sleepThread.isInterrupted();    //结果为false
        busyThread。isInterrupted();    //结果为true
    }
}

线程间的通信

线程等待/通知机制

等待/通知机制是任何java对象都具备的,因为这些方法被定义在Object类上

方法名称 描述
notify() 通知一个在对象上等待的线程,使其从wait()方法中返回,而返回的前提是该线程获取到了对象的锁
notifyAll() 通知所有等待在该对象的线程
wait() 调用该方法会使线程进入WAITING状态,只有等其它线程唤醒或者该线程被中断的时候才会从wait()方法返回,需要注意的是调用wait()方法之后将会释放锁
wait(long time) 等待一定的时间自动返回

等待/通知机制是指当一个线程调用了对象O的wait()方法之后,该线程进入等待状态,只有当其它线程调用notifyAll()或者notify()方法之后,该线程才会从wait()方法中返回,两个线程就通过对象O来进行交互。
调用wait()、notify()可以注意的细节:
- 使用wait()、notify()等方法之前要先对调用对象加锁
- 调用wait()方法之后,线程状态将从RUNNING变为WAITING,并将当前线程放入对象的等待队列中
- notify()和notifyAll()方法调用以后,等待线程依旧不会从wait()方法中返回,需要等notify()方法的线程释放锁之后线程才有机会从wait()放回

管道输入/输出流

和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存

public class Piped{
    public static void main(String[] args){
        PipeWriter out = new PipedWriter();
        PipedReader in = new PipedReader();
        //将输入流和输出流进行连接,否则在使用时会抛出IOException
        out.connect(in);
        Thread printThread = new Thread(new Print(in),"Print");
        printThread.start();
        int receive = 0;
        try{
            while(receive = System.in.read()) != -1){
                out.write(receive);
            }
        }finally{
            out.close();
        }
    }
    static class Print implements Runnable{
        private PipedReader in;
        public Print(PipedReader in){
            this.in = in;
        }
        public void run(){
            int receive = 0;
            while(receive = in.read())!= -1){
                System.out.print((char)receive);
            }catch(IOException ex){
            }
        }
    }
}

可以看到对于Piped类型的流,必须要先进行绑定,调用connect()方法将输入和输出流绑定起来。

ThreadLoacal的使用

ThreadLocal即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值
比如说线程A设置一个线程ID为A,set(A),则通过get()方法返回的就是A;线程B设置线程Id为B,则get()方法得到的就是B

有一些关于线程池和线程应用的具体代码在github上,欢迎大家指点迷津
https://github.com/WilderGao/ThreadStudy

猜你喜欢

转载自blog.csdn.net/qq_38095094/article/details/79249482