zoukankan      html  css  js  c++  java
  • Java Thread

    1,线程的基本概念

    线程是一个程序内部的顺序控制流。

    线程和进程的区别:(资源分配和处理器分配的基本单元)

    ①,每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销;
    ②,线程可以看成是轻量级进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小;
    ③,多进程:操作系统中能同时运行多个任务(程序);
    ④,多线程:在同一应用程序中有多个顺序流同时执行;

    Java线程是通过java.lang.Thread类来实现的。VM启动时会有一个由主方法(public static void main(){})所定义的线程。可以通过创建Thread的实例来创建新的线程。每个线程都是通过某个特定的Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。
    注意:
    要注意方法调用和运行线程的区别。方法调用,主程序会等方法运行完成程序再继续往下走;运行线程:相当于新建一个分支,主程序与线程并行执行。

    2,线程的创建和启动

    第一种:定义线程类实现Runnable接口

    Thread thread = new Thread(target);//target为Runnable接口类型

    Runnable中只有一个方法: public void run();用以定义线程运行体。

    使用Runnable接口可以为多个线程提供共享的数据。在实现Runnable接口的类的run方法定义中可以使用Thread的静态方法:

    public static Thread currentThread()获取当前线程的引用。

    例如:

    public static void main(String[] args) {
        Thread t = new Thread(new MyThread());
        t.start();//线程启动
        t.run();//方法调用
    }
    
    class MyThread implements Runnable {
        public void run() {
            ...
        }        
    }

    第二种:定义一个Thread的子类,并重写其run方法,如:

        class MyThread extends Thread {
            public void run() {...}
        }
    然后生成该类的对象:MyThread thread = new MyThread(...);

    例如:

    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();        
    }
    
    class MyThread extends Thread {
        public void run() {
            ...            
        }        
    }

    3,线程的状态

    3.1,线程的状态类型:

    ①,新建状态(New):创建了一个线程对象,它仅仅作为一个对象实例存在,JVM没有为其分配CPU时间片等线程运行资源;
    ②,就绪状态(Runnable):在处于创建状态的线程中调用start方法将线程的状态转换为就绪状态。这时,线程已经得到除CPU时间之外的其它系统资源,只等JVM的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU时间片的机会;
    ③,运行状态(Running):就绪状态的线程获取了CPU,执行程序代码;
    ④,阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    (1),等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池(waitting queue)中;
    (2),同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中;
    (3),其他阻塞:运行的线程执行sleep()或join()方法,或者发出I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超市、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态;
    ⑤,挂起状态(Suspend):可以通过调用suspend方法(已过时)将线程的状态转换为挂起状态。这时,线程将释放占用的所有资源,由JVM调度转入临时存储空间,直至应用程序调用resume方法(已过时)恢复线程运行;
    ⑥,死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

    3.2,线程状态转换图


    3.3,阻塞和挂起的区别:

    操作系统中挂起和阻塞的区别如下:
    (1),挂起是一种主动行为,因此恢复也应该要主动完成,而阻塞则是一种被动行为,是在等待事件或资源时任务的表现,你不知道他什么时候被阻塞(pend),也就不能确切 的知道他什么时候恢复阻塞。而且挂起队列在操作系统里可以看成一个,而阻塞队列则是不同的事件或资源(如信号量)就有自己的队列;
    (2),阻塞(pend)就是任务释放CPU,其他任务可以运行,一般在等待某种资源或信号量的时候出现。挂起(suspend)不释放CPU,如果任务优先级高就永远轮不到其他任务运行,一般挂起用于程序调试中的条件中断,当出现某个条件的情况下挂起,然后进行单步调试;
    (3),阻塞(pend)是task主动去等一个事件或消息,suspend是直接悬挂task,以后这个task和你没任何关系,任何task间的通信或者同步都和这个suspended task没任何关系了,除非你resume task;
    (4),任务调度是操作系统来实现的,任务调度时,直接忽略挂起状态的任务,但是会顾及处于pend下的任务,当pend下的任务等待的资源就绪后,就可以转为ready了。ready只需要等待CPU时间,当然,任务调度也占用开销,但是不大,可以忽略。可以这样理解,只要是挂起状态,操作系统就不在管理这个任务了;
    (5),挂起是主动的,一般需要用挂起函数进行操作,若没有resume的动作,则此任务一直不会ready。而阻塞是因为资源被其他任务抢占而处于休眠态。两者的表现方式都是从就绪态里“清掉”,即对应标志位清零,只不过实现方式不一样。 

    3.4,wait和sleep 的区别

    (1),这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
    (2),最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。Thread.Sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。
    (3),使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
       synchronized(x){
          x.notify()
         //或者wait()
       }
    (4),sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

    3.5,线程控制的基本方法

     isAlive() 判断线程是否还“活着”,即线程是否还未终止
     getPriority() 获得线程的优先级数值
     setPriority(int newPriority) 设置线程的优先级数值

     sleep(long millis)

     sleep(long millis, int nanos)

    将当前线程睡眠指定时间
     join() 调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行
     yield() 让出CPU,当前线程进入就绪队列等待调度
     wait() 当前线程进入线程等待池(wait pool)

     notify()

     notifyAll()

    唤醒对象的wait pool中的一个/所有等待线程

    3.5.1,线程优先级

    Java提供一个线程调度器来监控程序中就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。线程的优先级用数字标志,范围1~10,缺省优先级为5

    public static final int MIN_PRIORITY = 1;
    public static final int NORM_PRIORITY = 5;
    public static final int MAX_PRIORITY = 10;
    使用以下方法获得或这只线程对象的优先级:
    public final void setPriority(int paramInt) {
        checkAccess();
        if ((paramInt > 10) || (paramInt < 1)) {
            throw new IllegalArgumentException();
        }
        ThreadGroup localThreadGroup;
        if ((localThreadGroup = getThreadGroup()) != null) {
            if (paramInt > localThreadGroup.getMaxPriority()) {
                paramInt = localThreadGroup.getMaxPriority();
            }
            setPriority0(this.priority = paramInt);
        }
    }
    
    public final int getPriority() {
        return this.priority;
    }

    3.5.2,sleep/join/yield方法

    (1),sleep:可以调用Thread的静态方法sleep使得当前线程休眠,由于是静态方法,sleep可以直接由类名直接调用。

    例子:

    public class Test {
    
        public static void main(String[] args) {
            MyThread thread = new MyThread();
            thread.start();
            try {
                Thread.sleep(10000);//主线程睡眠            
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            thread.interrupt();//打断子线程(不推荐的方法,还有stop())
            //thread.flag = false;
        }
        
    }
    
    class MyThread extends Thread {
        boolean flag = true;
        public void run() {
            while(flag) {
                System.out.println("====" + new Date() + "====");
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    return;//sleep时出现异常则线程直接结束
                }
            }
        }
    }

    结果:

    ====Tue Jun 14 11:05:30 CST 2016====
    ====Tue Jun 14 11:05:31 CST 2016====
    ====Tue Jun 14 11:05:32 CST 2016====
    ====Tue Jun 14 11:05:33 CST 2016====
    ====Tue Jun 14 11:05:34 CST 2016====
    ====Tue Jun 14 11:05:35 CST 2016====
    ====Tue Jun 14 11:05:36 CST 2016====
    ====Tue Jun 14 11:05:37 CST 2016====
    ====Tue Jun 14 11:05:38 CST 2016====
    ====Tue Jun 14 11:05:39 CST 2016====

    源码:

    public static native void sleep(long paramLong) throws InterruptedException;
    
    public static void sleep(long paramLong, int paramInt)
            throws InterruptedException {
        if (paramLong < 0L) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if ((paramInt < 0) || (paramInt > 999999)) {
            throw new IllegalArgumentException(
                    "nanosecond timeout value out of range");
        }
        if ((paramInt >= 500000) || ((paramInt != 0) && (paramLong == 0L))) {
            paramLong += 1L;
        }
        sleep(paramLong);
    }

    (2),join:合并线程

    public class Test {
        public static void main(String[] args) {
            MyThread thread = new MyThread("abcde");
            thread.start();
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 3; i++) {
                System.out.println("I am main thread");
            }
        }
        
    }
    
    class MyThread extends Thread {
        MyThread(String name) {
            super(name);
        }
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println("I am " + getName());
            }
        }
    }

    结果:

    I am abcde
    I am abcde
    I am abcde
    I am main thread
    I am main thread
    I am main thread

    源码:

    public final void join() throws InterruptedException {
        join(0L);
    }
    
    public final synchronized void join(long paramLong)
            throws InterruptedException {
        long l1 = System.currentTimeMillis();
        long l2 = 0L;
        if (paramLong < 0L) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (paramLong == 0L) {
            while (isAlive()) {
                wait(0L);
            }
        }
        while (isAlive()) {
            long l3 = paramLong - l2;
            if (l3 <= 0L) {
                break;
            }
            wait(l3);
            l2 = System.currentTimeMillis() - l1;
        }
    }

    (3),yield:让出CPU,给其他线程执行的机会

    例子:

    public class Test {
        public static void main(String[] args) {
            MyThread t1 = new MyThread("t1");
            MyThread t2 = new MyThread("t2");
            t1.start();
            t2.start();
        }
    }
    
    class MyThread extends Thread {
        MyThread(String name) {
            super(name);
        }
        public void run() {
            for (int i = 1; i <= 100; i++) {
                System.out.println(getName() + ":" + i);
                if(i%10 == 0) {
                    yield();
                }
            }
        }
    }

    结果:

    每当被10整除的时候会yield
    ...
    t1:14
    t2:70
    t1:15
    ...
    ...
    t2:72
    t1:20
    t2:73
    ...
    t2:76
    t1:30
    t2:77
    ...

    源码:

    public static native void yield();

    4,线程同步

    在Java语言中,引入了对象互斥锁的概念,保证共享数据错做的完整性,每个对象对应于一个可称为”互斥锁“的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。
    关键字synchronized来与对象的互斥锁联系。当某个对象synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
    例一:

    public class Test implements Runnable {
        Timer timer = new Timer();
        public static void main(String[] args) {
            Test test = new Test();
            Thread t1 = new Thread(test);
            Thread t2 = new Thread(test);
            t1.setName("t1");
            t2.setName("t2");
            t1.start();
            t2.start();
        }
    
        @Override
        public void run() {
            timer.add(Thread.currentThread().getName());
        }
    }
    
    class Timer {
        private static int num = 0;
    
        public synchronized void add(String name) {
            // synchronized (this) {
            num++;
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }
            System.out.println(name + ", 你是第" + num + "个使用timer的线程");
            // }
        }
    }

    结果一(可能):

    t1, 你是第2个使用timer的线程
    t2, 你是第2个使用timer的线程

    加上synchronized控制

    结果二:

    t1, 你是第1个使用timer的线程
    t2, 你是第2个使用timer的线程

    例二:

    public class Test implements Runnable {
    
        public int flag = 1;
        static Object o1 = new Object();
        static Object o2 = new Object();
        
        @Override
        public void run() {
            System.out.println("flag = " + flag);
            if(flag == 1) {
                synchronized (o1) {//获得对象o1锁
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (o2) {//获得对象o2锁
                        System.out.println(1);
                    }
                }
            }
            if(flag == 0) {
                synchronized (o2) {//获得对象o2锁
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (o1) {//获得对象o1锁
                        System.out.println(0);
                    }
                }
            }
        }
        
        public static void main(String[] args) {
            Test test1 = new Test();
            Test test2 = new Test();
            test1.flag = 1;
            test2.flag = 0;
            Thread t1 = new Thread(test1);
            Thread t2 = new Thread(test2);
            t1.start();
            t2.start();
        }
    }

    结果:

    flag = 0
    flag = 1
    分别在等待获得o1,o2对象锁的,程序死锁,无法结束

    例三:

    哲学家吃饭问题

    5,生产者消费者问题

    public class Test {
        public static void main(String[] args) {
            SyncStack ss = new SyncStack();
            Producer p = new Producer(ss);
            Consumer c = new Consumer(ss);
            
            new Thread(c).start();
            new Thread(p).start();
            new Thread(p).start();//多加个生产者线程
            
        }
        
    }
    
    class WoTou {
        int id;
        WoTou(int id) {
            this.id = id;
        }
        public String toString() {
            return "WoTou:" + id;
        }
    }
    
    class SyncStack {
        int index = 0;
        WoTou[] arrWT = new WoTou[5];
        //push
        public synchronized void push(WoTou wt) {
            while(index == arrWT.length) {
                try {
                    this.wait();//阻塞
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.notifyAll();//唤醒其他所有线程
            arrWT[index] = wt;
            index++;
        }
        //pop
        public synchronized WoTou pop() {
            while (index == 0) {
                try {
                    this.wait();//阻塞
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.notifyAll();//唤醒其他所有线程
            index--;
            return arrWT[index];
        }
    }
    
    //生产者
    class Producer implements Runnable {
        SyncStack ss = null;
        
        Producer(SyncStack ss) {
            this.ss = ss;
        }
        
        @Override
        public void run() {
            for(int i=1; i<= 10; i++) {
                WoTou wt = new WoTou(i);//produce
                ss.push(wt);
                System.out.println("生产了:" + wt);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    //消费者
    class Consumer implements Runnable {
    
        SyncStack ss = null;
        Consumer(SyncStack ss) {
            this.ss = ss;
        }
        
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                WoTou wt = ss.pop();
                System.out.println("消费了:" + wt);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }    
    }

    结果:

    生产了:WoTou:1
    消费了:WoTou:1
    生产了:WoTou:1
    生产了:WoTou:2
    消费了:WoTou:2
    生产了:WoTou:2
    消费了:WoTou:2
    生产了:WoTou:3
    生产了:WoTou:3
    生产了:WoTou:4
    生产了:WoTou:4
    消费了:WoTou:4
    生产了:WoTou:5
    消费了:WoTou:5
    生产了:WoTou:5
    生产了:WoTou:6
    消费了:WoTou:5
    消费了:WoTou:6
    生产了:WoTou:6
    生产了:WoTou:7
    消费了:WoTou:6
    消费了:WoTou:7
    生产了:WoTou:8
    生产了:WoTou:9
    消费了:WoTou:8

     

  • 相关阅读:
    HashMap底层实现原理及面试常见问题
    Java面试题:==运算符与equals方法的区别
    SpringBoot基础,Java配置(全注解配置)取代xml配置
    实战SpringBoot Admin
    Java Object类中toString方法的重写
    java题
    1.5 安全性测试(功能)
    1.4 容量测试
    压力测试/极限测试(可靠性)
    1.2 性能测试(效率)
  • 原文地址:https://www.cnblogs.com/lemon-now/p/5583838.html
Copyright © 2011-2022 走看看