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

    什么是程序:有序严谨的指令集称为程序。

    什么是进程:程序的同时多运行称为进程。

    什么是线程:程序中不同的执行路经称为线程,线程是程序的最小执行单位。

    多线程:如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称为”多线程”

    • 多线程的好处:
      • 充分利用cpu的资源 编程简单
      • 简化编程模型 效率高
      • 带来良好的用户体验 易于资源共享

     

     

    新建状态:
    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

     

    就绪状态:
    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

     

    运行状态:
    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

     

    阻塞状态:
    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

     

    等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

     

    同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

     

    其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

     

    死亡状态:
    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

    在Java中创建线程的几种方式

    • 继承Thread类创建线程

    //继承Thread类创建线程
    public class MyThread extends Thread{
        public void run(){
            for(int i=0;i<100;i++){
        System.out.println(Thread.currentThread().getName());
            }
        }
    }
    public static void main(String[] args) {
            MyThread mt = new MyThread();
    //需使用start()方法启动线程,如果直接调用线程中run()方法会:
            //1.只有主线程一条执行路径2.依次调用了两次run()方法
            mt.start();}
    • 实现Runnable接口创建线程

    //实现Runnable接口创建线程
    public class MyRunnable implements Runnable {
    
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                System.out.println(Thread.currentThread().getName());
            }
        }
    public static void main(String[] args) {
        
            //创建线程对象
            MyRunnable mr = new MyRunnable();
            Thread t = new Thread(mr);
            t.start();
        }
    • 实现Callable接口创建Thread线程

    public class MyThread implements Callable<Integer> {
        
        @Override
        public Integer call() throws Exception {
    System.out.println("子线程:"+Thread.currentThread().getName());
            return null;
        }
    }
    public class Test {
        public static void main(String[] args) {
            FutureTask<Integer> ft = new FutureTask<Integer>(new MyThread());
            Thread t = new Thread(ft,"01");
            System.out.println("主线程:"+Thread.currentThread().getName());
            t.start();
        }
    }
    • 通过Future 创建线程
    public class CallableThreadTest implements Callable<Integer> {
        public static void main(String[] args)  
        {  
            CallableThreadTest ctt = new CallableThreadTest();  
            FutureTask<Integer> ft = new FutureTask<>(ctt);  
            for(int i = 0;i < 100;i++)  
            {  
                System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);  
                if(i==20)  
                {  
                    new Thread(ft,"有返回值的线程").start();  
                }  
            }  
            try  
            {  
                System.out.println("子线程的返回值:"+ft.get());  
            } catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            } catch (ExecutionException e)  
            {  
                e.printStackTrace();  
            }  
      
        }
        @Override  
        public Integer call() throws Exception  
        {  
            int i = 0;  
            for(;i<100;i++)  
            {  
                System.out.println(Thread.currentThread().getName()+" "+i);  
            }  
            return i;  
        }  
    }

    线程调度方法:

    • setPriority(int newPriority);更改线程的优先级

    线程优先级由1~10表示,1最低,默认优先级为5
    优先级高的线程获得CPU资源的概率较大

      1. MAX_PRIORITY:静态常量 表示最大优先级
      2. MIN_PRIORITY:静态常量 表示最小优先级
    • sleep(long millis);在指定毫秒数内让当前正在执行的线程休眠

    让线程暂时睡眠指定时长,线程进入阻塞状态
    睡眠时间过后线程会再进入运行状态
    millis为休眠时长,已毫秒为单位
    调用sleep()方法需处理InterruptedException异常

    • join(); 等待该线程终止

    Join(long millis);
    Join(long millis,int nanos);
    使当前线程暂停执行,等待其他线程结束后在继续执行本线程
    Millis:已毫秒为单位 的等待时长
    Nanos:要等待的附加纳秒时长
    需处理InterruptedException异常

    • yield(); 暂停当前正在执行的线程对象,并执行其他线程

    暂停当前线程,允许其他具有相同优先级的线程获运行机会
    该线程处于就绪状态,不转为阻塞状态
    只是提供一种可能,但是不能保证一定会实现礼让

    • interrupt(); 中断线程
    • isAlive();测试线程是否处于活动状态

    线程的同步:

    线程不同步会遇到的问题:(当多个线程共享同一资源时,一个线程未完成全部操作的时候,其他线程会修改数据,造成数据不安全问题。
    线程同步)

    • 使用synchronized修饰的方法控制对类成员变量的访问
    • 访问修饰符 synchronized 返回类型 方法名(参数列表){……}
      synchronized 访问修饰符 返回类型 方法名(参数列表){……}

    Synchronized()内为需同步的对象,通常为this
    效果与同步方法相同
    synchronized就是为当前的线程声明一把锁
    多个并发线程访问同一资源的同步代码块时
    同一时刻只能有一个线程进入synchronized(this)同步代码块
    当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
    当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码

    package demo01;
    //子线程在调用son方法的时候一上来就能获取同步监视器this
    //父线程在调用father函数的时候一上来就是获取不到
    //字父线程执行的四种情况: 子 父  父 子  子子 父父
    //wait:Object (导致线程阻塞,并释放对同步监视器的占用 )
    //notify:Object (唤醒线程的作用,提示后续等待该锁的线程,可以进行访问)
    public class FatherAndSon {
        boolean flag=true;
        
        public void son(){
            synchronized (this) {
                //子一定是先执行的
                if(!flag){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("儿子线程执行第"+(i+1)+"次");
                }
                flag=false;//1:把机会让给父线程 2、防止子线程再次执行
                this.notify();
            }
            
        }
        public  void father(){
            synchronized (this) {
                //父是后执行的
                if(flag){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for (int i = 0; i < 5; i++) {
                    System.out.println("老子线程执行第"+(i+1)+"次");
                }
                flag=true;//1:把机会让给子线程 2、防止父线程再次执行
                this.notify();
            }
            
        }
    }
    package demo01;
    
    //在Java中什么是所谓的同一资源 static
    //只存在一个对象的情况下  ticket 
    //多个对象: a.ticket b.ticket c.ticket
    //保证一种情况:
    //synchronized:方法(整个所修饰的方法体都是线程同步的范围)(this:)====>synchronized(this)
    //synchronized:代码块(代码块中的内容是线程同步的范围)
    public class Test {
       public static void main(String[] args) throws InterruptedException {
           final FatherAndSon fs=new FatherAndSon();
           
           Thread t1=new Thread(new Runnable(){  
             @Override
             public void run() { //子线程的线程体中
                 for (int i = 0; i < 10; i++) {
                      fs.son();
                 }    
             } 
           });
           t1.start();
           
          //父线程的线程体中
            for (int i = 0; i < 10; i++) {
              fs.father();
           }
       }  
    }

    线程池:

    尽量保证任务数不要超过最大线程数+阻塞队列的长度

    • 使用线程池的好处:
    1. 重用存在的线程,减少对对象创建,消亡的开销
    2. 有效控制最大并发数,提高系统资源使用率
    3. 定时执行,定期执行
    • 线程池所在的包:

    Java.util.concurrent
    顶级接口Executor,真正的线程池接口时ExecutorService
    Java.util.concurrent.Executors类提供创建线程池的方法

    自定义线程池:

    /**
     * 
     * 创建自定义线程池
     * 
     * */
    public class Test {
        public static void main(String[] args) {
            //创建自定义线程池   尽量保证:任务数不要超过最大线程数+阻塞队列的长度
            ThreadPoolExecutor executor = new ThreadPoolExecutor(5,7,300,TimeUnit.MINUTES,new ArrayBlockingQueue<Runnable>(4));
            
            for(int i=0;i<12;i++){
                executor.execute(new MyRunnable(i));
                System.out.println("线程池中线程数:"+executor.getPoolSize()+",对列中等待任务执行数:"+executor.getQueue().size()+",已经执行完的任务数:"+executor.getCompletedTaskCount());
            }
            executor.shutdown();
        }
    }
    
    
    class MyRunnable implements Runnable{
        int num;//第几个任务
         
        public MyRunnable(int num) {
            super();
            this.num = num;
        }
        @Override
        public void run() {
           System.out.println("正在执行任务"+num);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println("任务"+num+"执行完毕");
        }
        
        
    }

    后台线程的特点:

    1. 随着前台线程的运行而运行
    2. 随着前台线程的消亡而消亡
    3. SetDaemon():将线程设置为后台线程 GC 传入为true是将线程设置为后台线程,默认false
    4. isDaemon():判断是否是后台线程

     

  • 相关阅读:
    上传文件大小问题
    中检测到有潜在危险的 Request.Form 值
    Session
    C#浏览器中在线操作文档
    数据导入
    文件上传(插件版)和下载
    Leetcode练习(Python):递归类:面试题10- I. 斐波那契数列:写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下: F(0) = 0,&#160; &#160;F(1)&#160;= 1 F(N) = F(N
    Leetcode练习(Python):递归类:面试题07. 重建二叉树:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
    Leetcode练习(Python):递归类:面试题 16.11. 跳水板:你正在使用一堆木板建造跳水板。有两种类型的木板,其中长度较短的木板长度为shorter,长度较长的木板长度为longer。你必须正好使用k块木板。编写一个方法,生成跳水板所有可能的长度。 返回的长度需要从小到大排列。
    Leetcode练习(Python):递归类:面试题 08.06. 汉诺塔问题:在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。
  • 原文地址:https://www.cnblogs.com/big-data-sky/p/11031659.html
Copyright © 2011-2022 走看看