zoukankan      html  css  js  c++  java
  • 多线程(一)进程与线程

    进程vs线程

      在计算机中,我们把一个任务称为一个进程,浏览器就是一个进程,视频播放器是另一个进程,类似的,音乐播放器和Word都是进程。 某些进程内部还需要同时执行多个子任务。例如,我们在使用Word时,Word可以让我们一边打字,一边进行拼写检查,同时还可以在后台进行打印,我们把子任务称为线程。 进程和线程的关系就是:一个进程可以包含一个或多个线程,但至少会有一个线程。

    多线程

      Java语言内置了多线程支持:一个Java程序实际上是一个JVM进程,JVM进程用一个主线程来执行main()方法,在main()方法内部,我们又可以启动多个线程。此外,JVM还有负责垃圾回收的其他工作线程等。 和单线程相比,多线程编程的特点在于:多线程经常需要读写共享数据,并且需要同步。例如,播放电影时,就必须由一个线程播放视频,另一个线程播放音频,两个线程需要协调运行,否则画面和声音就不同步。因此,多线程编程的复杂度高,调试更困难。

    线程状态

    • 新建状态(New):新创建了一个线程对象
    • 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的 start() 方法,该状态的线程位于可运行的线程池中,变为可运行状态,这个时候,只要获取了 cpu 的执行权,就可以运行,进入运行状态。
    • 运行状态(Running): 就绪状态的线程从 cpu 获得了执行权之后,便可进入此状态,执行 run() 方法里面的代码。
    • 阻塞状态(Blocked):阻塞状态是线程因为某种原因失去了 cpu 的使用权,暂时停止运行,一直等到线程进入就绪状态,才有机会转到运行状态,阻塞一般分为下面三种:
      • 等待阻塞 :运行的线程执行了 wait() 方法, JVM 会把该线程放入线程等待池中,(wait() 会释放持有的锁 )
      • 同步阻塞:运行的线程在获取对象的同步锁时,如果该同步锁被其他线程占用,这时此线程是无法运行的,那么 JVM 就会把该线程放入锁池中,导致阻塞
      • 其他阻塞:运行的线程执行 sleep() 或者 join() 方法,或者发出了 I/O 请求,JVM 会把该线程置为阻塞状态,当 sleep() 状态超时、join() 等待线程终止或者超时、或者 I/O 处理完毕时,线程会重新进入就绪状态,(注意:sleep() 是不会释放本身持有的锁的)
    • 死亡状态(Dead):线程执行完了之后或者因为程序异常退出了 run() 方法,结束该线程的生命周期。

    线程调度 

      调整线程优先级

      Java 线程有优先级,优先级高的线程会获得较多的运行机会,Java 线程的优先级用整数表示,取值范围是 1~10 。线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。

    public class Thread implements Runnable {
        /**
         * The minimum priority that a thread can have.
         */
        public final static int MIN_PRIORITY = 1;
    
       /**
         * The default priority that is assigned to a thread.
         */
        public final static int NORM_PRIORITY = 5;
    
        /**
         * The maximum priority that a thread can have.
         */
        public final static int MAX_PRIORITY = 10;
    }
    View Code

      线程睡眠

      Thread.sleep(long millis) 方法,使线程转到阻塞状态。millis 参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。

    public class Thread implements Runnable {
        public static native void sleep(long millis) throws InterruptedException;
    }
    View Code

      线程等待

      Object 类中的 wait() 方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是 Object类中的方法,行为等价于调用 wait(0) 一样。
    public class Object {
        public final native void wait(long timeout) throws InterruptedException;
    }
    View Code

      线程让步

      Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。

      线程加入

      join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。

    public class TestJoin {
    
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println("hello");
                }
            };
            System.out.println(Thread.currentThread().getName() + " start");
            t.start();
            t.join();
            System.out.println(Thread.currentThread().getName() + " end");
        }
    
    }
    View Code
    执行結果:
    main start
    hello
    main end

      线程唤醒

      Object 类中的 notify() 方法,唤醒在此对象监视器上等待的单个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意的,并在对实现做出决定时发生,线程通过调用其中一个 wait() 方法,在对象的监视器上等待,直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程,被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争。类似的方法还有 notifyAll() ,唤醒再次监视器上等待的所有线程。

    public class TestWait {
        private Buffer mBuf = new Buffer();
    
        public void produce() {
            synchronized (this) {
                while (mBuf.isFull()) {
                    try {
                        System.out.println("full.stop produce");
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                mBuf.add();
                notifyAll();
            }
        }
    
        public void consume() {
            synchronized (this) {
                while (mBuf.isEmpty()) {
                    try {
                        System.out.println("empty.stop consume");
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                mBuf.remove();
                notifyAll();
            }
        }
    
        private class Buffer {
            private static final int MAX_CAPACITY = 3;
            private List<Integer> innerList = new ArrayList<>(MAX_CAPACITY);
    
            void add() {
                if (isFull()) {
                    System.out.println("full. stop add");
                } else {
                    innerList.add((int) (Math.random() * 100));
                    System.out.println(Thread.currentThread().toString() + " add");
                }
            }
    
            void remove() {
                if (isEmpty()) {
                    System.out.println("empty. stop remove");
                } else {
                    innerList.remove(0);
                    System.out.println(Thread.currentThread().toString() + " remove");
                }
            }
    
            boolean isEmpty() {
                return innerList.isEmpty();
            }
    
            boolean isFull() {
                return innerList.size() == MAX_CAPACITY;
            }
        }
    
        public static void main(String[] args) {
            TestWait t = new TestWait();
    
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        t.produce();
                    }
                }
    
            }.start();
    
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        t.consume();
                    }
                }
    
            }.start();
    
        }
    }
    View Code

    执行结果:

    Thread[Thread-0,5,main] add
    Thread[Thread-0,5,main] add
    Thread[Thread-1,5,main] remove
    Thread[Thread-0,5,main] add
    Thread[Thread-1,5,main] remove
    Thread[Thread-0,5,main] add
    Thread[Thread-0,5,main] add
    full.stop produce
    Thread[Thread-1,5,main] remove
    Thread[Thread-0,5,main] add
    full.stop produce

    线程实现方式

      1.继承 Thread 类,Thread 类也是实现的Runnable接口。

    public class TestThread extends Thread{
        
        @Override
        public void run() {
            System.out.println("继承Thread类");
        }
    
        public static void main(String[] args) {
            new TestThread().run();
        }
    
    }
    View Code

      2.实现 Runnable 接口

    public class TestRunable implements Runnable {
        
        @Override
        public void run() {
            System.out.println("实现Runnable接口");
        }
    
        public static void main(String[] args) {
            new TestRunable().run();
        }
    
    }
    View Code

      3.实现 Callable 接口,与 Future配合使用,有返回值

    public class TestCallable implements Callable<Object>{
                        
        @Override
        public Object call() throws Exception {
            System.out.println("实现Callable接口");
            return null;
        }
    
        public static void main(String[] args) {
            try {
                new TestCallable().call();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    View Code

      实现 Runnable 接口比继承 Thread 类所具有的优势:

    1.可以避免java中的单继承的限制
    2.线程池只能放入实现 Runable 或 callable 类线程,不能直接放入继承Thread 的类
  • 相关阅读:
    早晨突然想到的几句话
    VBA-工程-找不到工程或库-解决方案
    Mysql 服务无法启动 服务没有报告任何错误
    一道有趣的面试题
    异步和多线程
    异或运算
    线性代数解惑
    全文搜索引擎 Elasticsearch (一)
    HandlerExceptionResolver统一异常处理 返回JSON 和 ModelAndView
    MySQL 20个经典面试题
  • 原文地址:https://www.cnblogs.com/ryjJava/p/14398017.html
Copyright © 2011-2022 走看看