线程

作者: 面向星辰大海的程序员 | 来源:发表于2020-04-11 21:38 被阅读0次

    java线程多线程

    public class JavaStudy { 
    public static void main(String[] args) { 
    System.out.println("java线程学习");  
    //虚拟机线程管理的接口 
    ThreadMXBean threadMXBean =ManagementFactory.getThreadMXBean();     
    //取得线程管理信息       
    ThreadInfo[] threadInfo = threadMXBean.dumpAllThreads(false, false);  
    //Java天生多线程  
    for (ThreadInfo info : threadInfo) {    
    System.out.println("[" + info.getThreadId() + "]" + info.getThreadName()); 
    } 
      System.out.println("");
            System.out.println("beautiful line--------------" + Thread.currentThread());
            System.out.println("");
    
            UserThread userThread = new UserThread();
            userThread.start();
    
    //无返回值的任务   
         ImplRunnable implRunnable = new ImplRunnable();
            new Thread(implRunnable).start();
    
    //有返回值的任务
            ImplCallable implCallable = new ImplCallable();
            FutureTask<String> futureTask = new FutureTask<>(implCallable);
            new Thread(futureTask).start();
    
            try {
                System.out.println("return==" + futureTask.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
    
    }
    }
    
    
    
    1.png

    如何新建线程

     //How create Thread?
    
        //extends Thread
        public static class UserThread extends Thread {
    
            @Override
            public void run() {
                super.run();
                System.out.println(Thread.currentThread() + "I am extends Thread");
            }
        }
    
        //implements Runnable interface
        public static class ImplRunnable implements Runnable {
            @Override
            public void run() {
                System.out.println(Thread.currentThread() + "I am implements Runnable");
            }
        }
    
        /*implements Callable  ,allow return value**/
        public static class ImplCallable implements Callable<String> {
            @Override
            public String call() throws Exception {
                System.out.println(Thread.currentThread() + "I am implements Callable");
                return "I am ImplCallable return value";
            }
        }
    

    总结:java天生是一个多线层的语言,新建线程有三中方式
    1.new UserThread().start();
    2.new Thread(new Runnable()).start();
    3.new Thread(new FutureTask<>(new Callable())).start();

    Runnable和Callable是对任务的抽象
    对线程的抽象只有Thread
    调用start()方法才是真的启动了一个线程

    终止线程:
    强制中断的后果:

    协作式:
    interrupted()叫停线程
    isInterrupted()判断线程是否被叫停
    Thread.isInterrupted() 调用之后又会把标志位改为false

    UserThread.interrupted();
     public static class UserThread extends Thread {
    
            @Override
            public void run() {
                super.run();
                System.out.println( "I am extends Thread"+isInterrupted());这会打印false
                while (!isInterrupted()) {//被叫停就中断循环
                    System.out.println( "I am extends Thread  is Running");
                }
                System.out.println( "I am extends Thread  is Running" + isInterrupted());//这会打印true
            }
        }
    

    join():哪个线程调用就哪个线程优先执行
    yield():让出执行权,让出后系统可能又会选自己执行
    start():线程准备好了,但是不一定马上执行,等系统调度
    sleep():线程进入阻塞状态,sleep()时间到了或者interrupt()中断了就会进入就绪状态,属于线程的休眠,不会释放锁
    wait():线程进入阻塞状态,notify()或者notifyAll()进入就绪状态,不满足执行条件,等通知,也可以设定一定时间后继续执行,阻塞其间释放锁

    线程间的共享和协作:
    synchronized 内置锁,同一时间只能有一个线程访问锁
    用法 :
    锁方法

     public synchronized void countAdd() {//锁 this  就是当前对象
                count++;
            }
    

    锁对象

     private Object object = new Object();
            public void countAdd() {this
                synchronized (object) {//锁 自己new的对象,    也可这么写 synchronized (this)
                    count++;
                }
            }
    

    这两种方法没太大区别都叫对象锁

    提出疑问
    假如当前对象有两个属性,其中一个加锁访问,另一个不加锁,起两个线程访问对应的两个属性,即线程1访问属性1,2访问2,访问加锁的属性的同时,不加锁的属性能被另一个线程访问么?
    锁的是代码块,就是被synchronized包住的代码块或被synchronized修饰的方法
    不被锁的其他代码块能被其他线程访问,不受影响
    使用不同的对象锁不同的代码块,互不影响
    使用相同的对象锁不同的代码块,哪个线程先得锁哪个线程先得访问

    类锁
    静态方法 static 后面加synchronized 锁.class对象,其实就是对象锁

    总结:看似锁对象其实是作用在代码块,对象就像是一把锁的唯一标识

    什么是线程间的协作?
    条件不符时阻塞等待唤醒,修改条件唤醒阻塞线程,
    wait() notify() notifyAll() 如何理解和使用?

    等待通知的标准范式
    等待方:
    1.获取对象的锁
    2.检查条件,条件不满足时 wait()
    3.条件满足时,执行业务代码
    syn(对象){
    whilw(条件不满足){
    对象.wait()//调通wait()锁就会被释放
    }
    //执行业务代码
    }
    通知方:
    1.获取对象的锁
    2.修改条件
    3.通知等待方
    syn(对象){
    //执行业务代码,修改条件
    对象.notifyAll()//并不会释放
    }//代码块执行完之后才会释放

     public class CountBean {
            public static final int MaxCount = 5;
            private int i = 0;
    
            public synchronized void Iadd() {//线程1调用这
                i++;
                System.out.println(Thread.currentThread().getName() + "  i=" + i);
                notifyAll();
            }
    
            public synchronized int getI() throws InterruptedException {//线程2调用这
                while (i < MaxCount) {
                    wait();//阻塞 等其他线程占有时调用notify或notifyAll方法,并再次获得锁后,条件一直符合,就一直循环阻塞
                }
                return i;
            }
        }
    

    ThreadLocal:线程隔离

     static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {//静态修饰保证全虚拟机只有一份
            @Override
            protected Integer initialValue() {//初始化值
                return 0;
            }
        };
    
        private Runnable runnable = new Runnable() {//假如三个线程都跑这个任务
            @Override
            public void run() {
                for (int k = 0; k <3 ; k++) {//看起来是使用了一个threadLocal对象,但是每个线程之间没有影响
                    int i = threadLocal.get();
                    i++;
                    threadLocal.set(i);
                    System.out.println(Thread.currentThread().getName() + "   i=" + threadLocal.get());//正常的打印出三个线程的累加
                }
    
            }
        };
    
    2.png

    显示锁:Lock
    synchronized 获取锁没有中断,就是没拿到锁我就等着,没有尝试获取锁
    Lock是个接口,jdk有几个实现类:

    3.jpg

    使用范式

      private Lock lock = new ReentrantLock();
        public void main() {
            lock.lock();
            try {
                //业务代码,有可能发生异常
            } finally {//不管业务代码怎么样,一定能释放锁
                lock.unlock();
            }
        }
    

    ReentrantLock 可重入锁
    什么是可重入锁:线程拿到锁,没释放锁,再次访问同一个锁,还能拿到这个锁,拿多少次就累加记录,这就是可重入锁,synchronized也是可重入锁,在递归调用有锁的方法时防止自己锁死自己。

    锁的公平非公平:
    公平:先取锁的线程一定先拿到锁
    非公平:先取锁的线程不一定先拿到(性能更好 synchronized是非公平,ReentrantLock 默认非公平 构造传true就是公平锁)

    为什么非公平更好?
    线程挂起唤醒,上下文切换大约需要20000个时间周期,挂起一次20000,唤醒一次20000,
    如果是公平锁就得等前面先挂起的线程先拿锁执行,如果刚好可以拿锁不用挂起再唤醒就可以省去40000个时间周期,整体来说非公平锁性能优于公平锁,大概的说。

    排他锁:有且只有一个线程能拿到的锁(synchronized)

    ReentrantReadWriteLock 读写锁
    读:允许有多个线程拿锁读取文件数据,排斥写线程,防止数据不一致
    写:只允许一个线程写数据,排斥其他线程,防止数据不一致

    为什么会有读写锁?
    在生活中读比写的比例大约在10:1左右,读写分离,资源分配合理

     public class Merchandise {
            private final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
            private final Lock getLock = reentrantReadWriteLock.readLock();//读锁    10个线程访问
            private final Lock setLock = reentrantReadWriteLock.writeLock();//写锁      1个线程访问
    
            public  void setPrice() throws InterruptedException {
    
                setLock.lock();
                try {
                    Thread.sleep(5);//模拟写花的时间
                } finally {
                    setLock.unlock();
                }
            }
    
            public  void getPrice() throws InterruptedException {
                getLock.lock();
                try {
                    Thread.sleep(5);//模拟读花的时间
                } finally {
                    getLock.unlock();
                }
            }
        }
    

    Condition:

     public class Express {
            public static final String ST_shanghai = "shanghai";
            public static final String ST_beijing = "beijing";
            private String currentStation = ST_shanghai;
            private int distance = 0;
            private Lock lock = new ReentrantLock();//一把锁多个开关
            private Condition stationCondition = lock.newCondition();//开关1
            private Condition distanceCondition = lock.newCondition();//开关2
    
            public void changeStation(String station) {
                lock.lock();
                try {
                    currentStation = station;//条件改变
                    stationCondition.signal();//开关1发出一个通知
                } finally {
                    lock.unlock();
                }
            }
    
            public void changeDistance(int distance) {
                lock.lock();
                try {
                    this.distance = distance;//条件改变
                    distanceCondition.signalAll();//开关2发出一个通知
                  //  distanceCondition.signal();//开关2发出一个通知
                } finally {
                    lock.unlock();
                }
            }
    
            public void waitStation() {
                lock.lock();
                try {
                    while (currentStation.equals(ST_shanghai)) {
                        try {
                            System.out.println(Thread.currentThread().getName() + "  waitStation");
                            stationCondition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(Thread.currentThread().getName() + "  Station is Change");
                } finally {
                    lock.unlock();
                }
            }
    
            public void waitDistance() {
                lock.lock();
                try {
                    while (distance < 100) {
                        try {
                            System.out.println(Thread.currentThread().getName() + "  waitDistance");
                            distanceCondition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(Thread.currentThread().getName() + "   Distance is Change");
                } finally {
                    lock.unlock();
                }
    
            }
        }
    

    线程池的优点 :
    1.复用,节约资源 2.响应速度块(省掉创建的时间)3.提高线程的可管理

    简单的线程池:
    public class ThreadPool {
        private int coreThreadCount = 5;
        private int defaultTaskCount = 100;
        private WorkThread[] workThreads;
        private final BlockingQueue<Runnable> taskQueue;
    
        public ThreadPool() {
            taskQueue = new ArrayBlockingQueue<>(defaultTaskCount);
            workThreads = new WorkThread[coreThreadCount];
            for (int i = 0; i < coreThreadCount; i++) {
                workThreads[i] = new WorkThread();
                workThreads[i].start();
            }
            System.out.println("cpuCoreCount=" + Runtime.getRuntime().availableProcessors());
        }
    
        public void execute(Runnable task) {
            try {
                taskQueue.put(task);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public void destroy() {
            for (int i = 0; i < coreThreadCount; i++) {
                workThreads[i].interrupt();
                workThreads[i] = null;
    
            }
            taskQueue.clear();
        }
    
        public class WorkThread extends Thread {
            @Override
            public void run() {
                super.run();
                Runnable runnable = null;
    
                try {
                    while (!isInterrupted()) {
                        runnable = taskQueue.take();
                        if (runnable != null) {
                            runnable.run();
                        }
                        runnable = null;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
    
            }
        }
    }
       ThreadPool threadPool = new ThreadPool();
    
            for (int i = 0; i < 10; i++) {
                threadPool.execute(new ImplRunnable("Task" + i));
            }
        }
    
        public static class ImplRunnable implements Runnable {
            private String taskName;
    
            public ImplRunnable(String taskName) {
                this.taskName = taskName;
            }
    
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("complete:" + taskName);
            }
        }
    

    jdk线程池:

    jdkThreadPool.png
    corePoolSize: 核心线程数,线程池保留的最小线程数
    maximumPoolSize: 最大线程数,当任务队列装不下之后就开始创建新的线程
    keepAliveTime: 空闲线程存活时间,保留的线程数等于核心线程数
    unit: 时间单位,对应keepAliveTime
    workQueue: 任务缓冲阻塞队列,核心线程都在工作时候添加到队列
    threadFactory: 搞线程名的
    handler: 溢出策略(饱和策略,拒绝策略),当最大线程数也满了之后开始响应策略

    jdk提供的几种策略:

    reject.png
    AbortPolicy:直接抛出异常(jdk默认)
    CallerRunsPolicy:哪个线程提交的任务它自己执行
    DiscardPolicy:直接丢到要放进来的任务
    DiscardOldestPolicy:丢弃阻塞队列最先进去的任务

    自定义策略:自己实现RejectedExecutionHandler

    BlockingQueue:

    blockingqueue.png
    add remove
    offer poll
    put take 阻塞

    常用的BlockingQueue:
    ArrayBlockingQueue:
    LinkedBlockingQueue:

    合理使用分配线程:
    Cpu密集型:不要超过Cpu同时运行的线程数 Runtime.getRuntime().availableProcessors()取得线程数
    IO密集型 :2*Cpu同时运行的线程数
    混合型:拆分成以上两个

    AsyncTask:

    volatile 保证可见性 不保证原子性

    相关文章

      网友评论

          本文标题:线程

          本文链接:https://www.haomeiwen.com/subject/jqmpmhtx.html