zoukankan      html  css  js  c++  java
  • Java多线程

    线程的状态

    新建(New)、可运行(Runnable)、阻塞(Blocking)、无限期等待(Waiting)、有限期等待(Timed-Waiting)、死亡(Terminated)

    使用线程

    • 实现Callable接口
    • 实现Runnable接口
    • 继承Thread类

    线程机制

    用户线程与守护(Daemon)线程

    守护线程时提供通用服务的线程(例如垃圾回收线程),优先级很低
    通过Thread对象的setDaemon(boolean)方法设置守护线程与用户线程,注意此方法必须在线程对象的start方法之前调用,否则会抛出java.lang.IllegalThreadStateException异常
    守护线程创建的线程默认是守护线程,用户线程同理。
    守护线程是为了服务用户线程而存在的,所有用户线程结束之后,jvm就退出了,守护线程自然也结束,所以守护线程的结束时间不确定

    睡眠

    线程的sleep方法(静态方法),休眠当前线程,当前线程进入timed-waiting状态

    yield

    线程对象的yield方法表明该线程已经完成了最重要的工作,尝试让出cpu占用,把cpu使用权让给同优先级的其他线程

    中断

    interrupt()

    通过调用线程对象的interrput方法中断该线程,如果该线程处于等待/阻塞状态,会抛出java.lang.InterruptedException异常。
    该方法不能中断处于I/O阻塞或synchronized阻塞的线程
    线程对象的该方法被调用后,会设置阻断标志为true,通过interrupted()方法检查是否有人调用了本线程的interrupt方法

    interrupted()

    检查是否有人调用了本线程的interrupt方法

    中断的应用

    线程池的shutdownNow方法就是通过调用所有工作线程的interrupt方法来中断工作线程

    线程池

    https://www.cnblogs.com/darknessplus/p/10359256.html

    线程安全的实现方法

    不可变

    互斥同步

    synchronized关键字与ReentrantLock

    都是可重入锁
    synchronized关键字由jvm实现,ReentrantLock由jdk实现
    ReentrantLock提供比synchronized更高级的功能

    • 等待可中断
    • 公平锁
    • 选择性通知

    非阻塞同步

    无同步

    线程本地存储

    ThreadLocal类
    ThreadLocal 不是用来协调多线程的,而是为同一个线程的不同方法提供共享变量
    一个线程的ThreadLocal变量在任何方法内都是可见的
    线程Thread保存了ThreadLocalMap,ThreadLocalMap保存了本线程的所有ThreadLocal数据
    ThreadLocal提供的方法

    public T get() { }
    public void set(T value) { }
    public void remove() { }
    protected T initialValue() { }
    

    线程之间的协调

    多线程协作解决问题时,需要对他们的执行顺序进行协调

    join

    调用另一个线程对象的join方法表示本线程等待另一个线程执行完成后再继续执行

    wait、notify、notifyAll

    wait、notify是Object类提供的final方法,不可重写。
    通过调用某个对象的wait方法,释放本线程对该对象持有的锁,进入阻塞状态。
    另一个线程调用该对象的notify方法,唤醒该对象的锁的阻塞队列中的一个线程,notifyAll方法唤醒该对象的等待队列的所有线程。
    wait期间,线程会释放锁,不然会死锁。
    配合synchronized使用,再同步代码中使用。

    wait与sleep的区别:

    • wait是Object方法,sleep是Thread的静态方法
    • wait会释放锁,sleep不会

    生产者消费者问题:

    public class Wait {
    
        public static void main(String[] args) throws InterruptedException {
            LinkedList<Integer> list = new LinkedList<Integer>();
            Factory factory = new Factory();
            factory.setList(list);
            factory.setMaxSize(10);
            for(int i=1;i<=10;i++)
            {
                Producer producer = new Producer();
                producer.setNum(i);
                producer.setFactory(factory);
                producer.start();
    
                Consumer consumer = new Consumer();
                consumer.setNum(i);
                consumer.setFactory(factory);
                consumer.start();
            }
        }
    }
    class Producer extends Thread{
        private AbstractFactory factory;
        private int num;
    
        public void setNum(int num) {
            this.num = num;
        }
    
        public void setFactory(AbstractFactory factory) {
            this.factory = factory;
        }
    
        @Override
        public void run() {
            factory.produce(num);
        }
    }
    class Consumer extends Thread{
        private AbstractFactory factory;
        private int num;
    
        public void setFactory(AbstractFactory factory) {
            this.factory = factory;
        }
    
        public void setNum(int num) {
            this.num = num;
        }
    
        @Override
        public void run() {
           factory.consume(num);
        }
    }
    class Factory implements AbstractFactory{
        private List<Integer> list;
        private int maxSize;
    
        public void setList(List<Integer> list) {
            this.list = list;
        }
    
        public void setMaxSize(int maxSize) {
            this.maxSize = maxSize;
        }
    
        public void produce(int num) {
            synchronized (list)
            {
                while(num+list.size()>maxSize)
                {
                    try {
                        System.out.println("仓库已满。要生产的"+num+",库存容量"+maxSize);
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                int size=list.size();
                for(int i=size;i<size+num;i++)
                {
                    list.add(i);
                }
                System.out.println("生产"+num+"件,总共"+list.size()+"件");
                list.notifyAll();
            }
        }
    
        public void consume(int num) {
            synchronized (list)
            {
                while(list.size()-num<0)
                {
                    try {
                        System.out.println("仓库已空。要消费的"+num+",库存容量"+list.size());
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                int size=list.size();
                for(int i=size-1;i>=size-num;i--)
                {
                    list.remove(i);
                }
                System.out.println("消耗"+num+"件,总共"+list.size()+"件");
                list.notifyAll();
            }
        }
    }
    interface AbstractFactory
    {
        void produce(int num);
        void consume(int num);
    }
    

    await、signal、signalAll

    对应着Object的wait和notify方法,Lock也有类似的机制。
    通过Lock.newCondition()方法获取一个condition对象,通过condition对象的await和signal方法进行线程同步。
    在使用condition对象的await和signal方法之前必须获得重入锁,调用signal方法后最好释放重入锁。

    JUC包

    AQS

    CountdownLatch

    控制一个线程等待其他线程

    public static void main(String[] args) throws InterruptedException {
            final CountDownLatch count = new CountDownLatch(10);
            for(int i=0;i<10;i++){
                new Thread(){
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName());
                        count.countDown();
                    }
                }.start();
            }
            count.await();
            System.out.println("END");
        }
    

    输出:

    Thread-0
    Thread-2
    Thread-3
    Thread-1
    Thread-5
    Thread-4
    Thread-6
    Thread-7
    Thread-8
    Thread-9
    END
    

    Semaphore

    信号量,控制并发线程数

    public static void main(String[] args) {
            Semaphore semaphore = new Semaphore(1);
            for (int i = 0; i < 10; i++) {
                new Thread() {
                    @Override
                    public void run() {
                        try {
                            semaphore.acquire();
                            System.out.println(Thread.currentThread().getName() + "正在运行");
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            System.out.println(Thread.currentThread().getName() + "结束");
                            semaphore.release();
                        }
                    }
                }.start();
            }
        }
    

    输出:

    Thread-5正在运行
    Thread-5结束
    Thread-0正在运行
    Thread-0结束
    Thread-3正在运行
    Thread-3结束
    Thread-8正在运行
    Thread-8结束
    Thread-6正在运行
    Thread-6结束
    Thread-2正在运行
    Thread-2结束
    Thread-7正在运行
    Thread-7结束
    Thread-1正在运行
    Thread-1结束
    Thread-4正在运行
    Thread-4结束
    Thread-9正在运行
    Thread-9结束
    

    其他组件

    FutureTask

    获得任务的返回值

    BlockingQueue

    阻塞队列,在线程池里有应用

    乐观锁和悲观锁

    悲观锁

    对数据更新的冲突持保守态度,认为总会发生冲突。策略是在处理数据时加锁

    乐观锁

    乐观锁认为发生冲突的情况比较少,不加锁,而是在更新数据的时候检查是否发生了冲突。

    乐观锁的两种实现

    版本号机制

    为数据库表增加版本号字段,每次更新数据版本号+1。在修改数据前获取版本号,提交修改时检查两次版本号是否一致,如不一致说明数据更新发生了冲突。

    CAS

    Compare And Swap
    现值V、旧值A、新值B,当且仅当V==A,更新B到数据库
    硬件实现的原子操作

    乐观锁的缺点

    ABA问题

    CAS,V==A并不能说明V没有发生过改变

    自旋锁开销大

    CAS与synchronized使用场景

    CAS适用读多写少,synchronized适用写多(冲突多)

    synchronized和ReenTrantLock的区别

    • 都是可重入
    • synchronized由jvm实现,ReenTrantLock时jdk api
    • ReenTrantLock高级功能:中断等待、公平锁、选择性通知
    • 两者性能相差无几
  • 相关阅读:
    nodejs使用superagent写爬虫dns超时
    react部署nginx刷新路由404
    ubuntu安装mongodb添加账户以及远程连接
    laravel使用layui富文本编辑器layedit上传图片419解决办法
    编写前端统计网页流量,来源,停留时间等
    laravel模版共用数据解决方法
    解决MySQL导入中文乱码
    yii2 jui DatePicker widget 设置显示默认时间
    装饰器
    python函数计时器(通过装饰器实现)
  • 原文地址:https://www.cnblogs.com/darknessplus/p/10357698.html
Copyright © 2011-2022 走看看