多线程管理


前言

例如:随着计算机的不断发展,多线程这门技术也越来越重要,并且对于java来说多线程也是必须掌握的一门技术,对于我们来说是必不可少的

一、线程与进程的区别

进程:指内存中的一个应用程序,每个进程都有一个独立的内存空间,它是由多个线程组成的。 线程:指进程中的一个执行路径,共享一个内存空间,并发执行。java中使用的大多数都是抢占式调度。

二、并发和并行

1、同步和异步

同步:排队执行 , 效率低但是安全。
异步:同时执行 , 效率高但是数据不安全。

2、并发和并行

并发:指的是两个或两个以上的线程在一段时间内完成的数量。
并行:指的是两个及两个以上的线程在某一时刻同时完成的数量。

三、多线程的三个使用方法

1、Thread

继承从父类中得来的Thread,但用的不多。


new Thread(new MyThread()).start();//创建一个新线程并开始。
        for (int i = 0; i <5 ; i++) {//主线程开始运行
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
//静态内部类
static class MyThread extends Thread {
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("我爱Java" + i);
            }
        }
    }

2、Thread常用的方法

方法描述
sleep​(long millis)导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。
setName​(String name)将此线程的名称更改为等于参数 name 。
start()导致此线程开始执行; Java虚拟机调用此线程的run方法。
run()如果此线程是使用单独的Runnable运行对象构造的,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。
interrupt()中断此线程。 相当于做一个标记符。
isDaemon()测试此线程是否为守护程序线程。
getName()返回此线程的名称。
 //如何获取线程的名称
       System.out.println(Thread.currentThread().getName());
       //两种设置线程名称的方式
       Thread t = new Thread(new MyRunnable());
       t.setName("wwww");
       t.start();
       new Thread(new MyRunnable(),"锄禾日当午").start();
       //不设置的有默认的名字
       new Thread(new MyRunnable()).start();
      
//如何中断线程
      Thread t1 = new Thread(new MyRunnable());//线程运行
       t1.setDaemon(true);
       t1.start();

       for (int i = 0; i < 5; i++) {
           System.out.println(Thread.currentThread().getName()+" "+i);
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
              // Thread.interrupted();
               e.printStackTrace();
           }
       }
       t1.interrupt();

   static class MyRunnable implements  Runnable{

       @Override
       public void run() {
           for (int i = 0; i < 10; i++) {
               System.out.println(i);
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   //return;
                   e.printStackTrace();
               }
           }
       }
   }
       //线程分为守护线程和用户线程
       //用户线程:当一个进程不包含任何的存活的用户线程时,进行结束
       //守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。
       Thread t1 = new Thread(new MyRunnable());
       //设置守护线程
       t1.setDaemon(true);
       t1.start();
       for (int i = 0; i < 5; i++) {
           System.out.println(Thread.currentThread().getName()+":"+i);
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
  

3、Runnable

实现接口Runnable接口,用的最多的方式
实现Runnable与继承Thread相比有如下优势
1、通过创建任务,然后给线程分配任务的方式实现多线程,更适合多个线程同时执行任务的情况
2、可以避免单继承所带来的局限性
3、任务与线程是分离的,提高了程序的健壮性
4、线程池技术,接受Runnable类型的任务,不接受Thread类型的线程

//和上面的差不多
new Thread(new MyRunnable()).start();
        for (int i = 0; i <5 ; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
//静态内部类
 static class MyRunnable implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("我爱Java" + i);
            }
        }
    }

4、Callable

它带返回值,Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

public interface Callable<V> {   
 	V call() throws Exception
 }//需要扔出异常,用call方法实现
 class XXX implements Callable<T>{
 @Override            
 public <T> call() throws Exception {
        return T;
  }}
  //1.创建FutureTask对象 , 并传入第一步编写的Callable类对象FutureTask<Integer> future = new FutureTask<>(callable);
 //2.通过Thread,启动线程newThread(future).start();

四、多线程的不安全的解决办法

1、同步代码块

data = pd.read_csv(
    Runnable rn = new MyRun();
        new Thread(rn).start();
        new Thread(rn).start();
        new Thread(rn).start();

    }
    static class MyRun implements Runnable{
        public MyRun() {
        }

        private int count = 10;
        private  Object o = new Object();//添加一个锁,标记不能是各自有各自的锁,要不然会出现事情,
        @Override//意思就是不能再run里面加标记,加的话那么没个线程都会有自己的锁了,会按照自己的锁来走
        public void run() {
            synchronized (o){
                while (count>=0){
                    System.out.println(Thread.currentThread().getName() +"\n ");

                    try {
                        Thread.sleep(1000);
                        System.out.println("剩余票数:"+count);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                }
            }
        }

2、同步方法

即增加一个方法来做标记

Object o = new Object();
        //线程不安全
        //解决方案2  同步方法
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        @Override
        public void run() {

            while (true) {
                boolean flag = sale();
                if(!flag){
                    break;
                }
            }
        }
        public synchronized boolean sale(){
            if (count > 0) {
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                return true;
            }
                return false;

        }

3、显示锁

Lock l = new ReentrantLock(true);即增加一个锁住的方法和解锁的方法

 public static void main(String[] args) {
        Runnable rn = new MyRun();
        new Thread(rn).start();
        new Thread(rn).start();
        new Thread(rn).start();

    }
    static class MyRun implements Runnable{
        public MyRun() {
        }

        private int count = 10;
        private  Object o = new Object();//添加一个锁,标记不能是各自有各自的锁,要不然会出现事情,
        //意思就是不能再run里面加标记,加的话那么没个线程都会有自己的锁了,会按照自己的锁来走
        Lock l = new ReentrantLock(true); //true是公平锁,即锁住的时候只能有一个线程来做。
        public void run() {
            //synchronized (o){
                while (count>=0){
                    l.lock();
                    System.out.println(Thread.currentThread().getName() +"\n ");

                    try {
                        Thread.sleep(1000);
                        System.out.println("剩余票数:"+count);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    l.unlock();

五、线程死锁

指两个线程及两个线程以上相互调用对方所持有的资源,由于Synchronized的特点,一个线程获得一个锁,在该线程释放这个锁之前其他的线程是获取不到中国所的,所以会一直死等下去。

如何避免

按照线程相同的顺序加锁。
或者在取锁的过程当中加一定的时限。

六、线程的六种状态

状态概述
Blocked线程的状态被堵塞
New创建未启动的线程状态
Runnable可运行的线程状态
Terminated终止的线程状态
Timed_Waiting具有等待时间的线程的状态
Wating等待线程的状态

七、等待与唤醒

在java.lang.Object包上,主要的几个方法

方法概述
wait()导致当前线程等待它被唤醒,通常是 通知或 中断 。
notify()唤醒正在此对象监视器上等待的单个线程。
notifyAll()唤醒等待此对象监视器的所有线程。
wait​(long timeoutMillis)导致当前线程等待它被唤醒,通常是 通知或 中断 ,或者直到经过一定量的实时。

当一个线程调用wait()方法的时候,就会进入等待状态,直到被其他的线程用notify唤醒才开始进行工作。

八、线程池

即储存线程的容器,普通线程创建的流程:创建线程→创建任务→执行任务→关闭。
而线程池节省了创建和关闭线程池的任务。

四种线程池

(1)缓存线程池

//缓存线程池
        //无限制长度
        //任务加入后的执行流程
        //1判断线程池是否存在空闲线程  2存在则使用   3不存在则创建线程并使用

        //向线程池中加入新的任务
        ExecutorService service = Executors.newCachedThreadPool();
        //指挥线程池执行新的任务
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }

        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }

        });service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }

        });

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

(2)定长线程池

 /*定长线程池
    长度是指定的线程池
    加入任务后的执行流程
        1 判断线程池是否存在空闲线程
        2 存在则使用
        3 不存在空闲线程  且线程池未满的情况下  则创建线程  并放入线程池中  然后使用
        4 不存在空闲线程  且线程池已满的情况下  则等待线程池的空闲线程
    **/
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });

(3)单线程线程池

/*单线程线程池
    执行流程
        1 判断线程池的那个线程是否空闲
        2 空闲则使用
        3 不空闲则等待它空闲后再使用
    **/
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });

(4)周期定长线程池

/*周期任务  定长线程池
    执行流程
        1 判断线程池是否存在空闲线程
        2 存在则使用
        3 不存在空闲线程  且线程池未满的情况下  则创建线程  并放入线程池中  然后使用
        4 不存在空闲线程  且线程池已满的情况下  则等待线程池的空闲线程

        周期性任务执行时
                定时执行 当某个任务触发时  自动执行某任务
    **/
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        //定时执行一次
        //参数1:定时执行的任务
        //参数2:时长数字
        //参数3:2的时间单位    Timeunit的常量指定
       /* scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        },5, TimeUnit.SECONDS);      //5秒钟后执行*/

        /*
        周期性执行任务
            参数1:任务
            参数2:延迟时长数字(第一次在执行上面时间以后)
            参数3:周期时长数字(没隔多久执行一次)
            参数4:时长数字的单位
        * **/
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        },5,1,TimeUnit.SECONDS);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值