• Java语言基础多线程


    多线程

        进程:正在进行中的程序(直译);
                           
        线程:就是进程中的一个负责程序执行的控制单元(执行路径);一个进程中可以有多个执行路径,称为多线程。
                        一个进程中,至少要有一个线程。
        多线程存在的意义:开启多个线程,是为了同时运行多部分代码;
                        每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。
        多线程优点:解决了多部分同时运行的问题。
        多线程弊端:线程太多会导致效率的降低。
        其实应用程序的执行都是cpu在做着快速的切换完成的,这个切换是随机的。

        JVM启动时就启动了多个线程,至少有两个线程可以分析得出来:
        1.执行main函数的线程;
                该线程的任务代码都定义在main函数中;
        2.负责垃圾回收的线程。

        创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码就,实现同时运行
        而运行的指定代码就是这个执行路径的任务;
        JVM创建的主线程的任务都定义在了主函数中;而自定义的线程:Thread类用于描述线程,
        线程是需要任务的,所以Thread类也有对任务的描述,这个任务就通过Thread类中的run方法来实现,也就是说,
        run方法就是封装自定义线程运行任务的函数。run方法中定义的就是线程要运行的代码。
        开启线程是为了运行指定代码,所以只有继承Thread类,并覆写run方法,将运行的代码定义在run方法中。

        如何创建线程?
        1.继承Thread类
            步骤:1.定义一个类继承Thread类;
                        2.覆盖Thread类中的run方法;
                        3.直接创建Thread的子类对象创建线程;
                        4.调用start方法,开启线程并调用线程的任务run方法执行。

        可以通过Thread的getName获取线程的名称 Thread-(从0开始)。
        继承Thread类示例:
       

    class Demo extends Thread
        {
            private String name;
            Demo(String name)
            {
                super(name);
                //this.name=name;
            }
            public void run()
            {
                for (int x=0;x<10 ;x++ )
                {
                    System.out.println(name+"...........x="+x+" name="+Thread.currentThread().getName());
                }
            }
        }
    
        class ThreadDemo3
        {
            public static void main(String[] args) 
            {
                Demo d1=new Demo("旺财");
                Demo d2=new Demo("xiaoqiang");
                d1.start();//开启线程,调用run方法
                d2.start();
                System.out.println("over="+Thread.currentThread().getName());
            }
        }
    
    

    1.定义类,实现Runnable接口;
    2.覆盖接口中的run方法,将线程的任务代码封装到run方法中;
    3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递;
        原因:因为线程的任务都封装在Rnnable接口子类对象的Run方法中,所以要在线程对象创建时就必须明确要运行的任务。
    4.调用线程对象的start方法,开启线程。
    Runnable:它的出现,仅仅是将线程的任务进行了对象的封装。

    实现Runnable接口的优点:
    1.将线程的任务从线程的子类中分离出来,进行了单独的封装;
        按照面向对象的思想将任务封装成对象;
    2.避免了Java单继承的局限性;
    所以,创建线程的第二种方法较为常用。

    实现Runnable接口示例:

    class Demo implements Runnable    //准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行
                                                            //通过接口的形式完成
    {
        public void run()
        {
            show();
        }
        public void show()
        {
            for (int x=0;x<10 ;x++ )
            {
                System.out.println(Thread.currentThread().getName()+"......"+x);
            }
        }
    }
    
    class ThreadDemo
    {
        public static void main(String[] args) 
        {
            Demo d=new Demo();
            Thread t1=new Thread(d);
            Thread t2=new Thread(d);
            t1.start();
            t2.start();
        }
    }
    
    

    线程安全问题产生的原因:
    1.多个线程在操作共享的数据;
    2.操作共享数据的线程代码有多条;
    当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,
    就会导致线程安全问题的产生。

    解决思路:
    将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的,
    必须要当前线程将这些代码执行完毕后,其他线程才可以参与运算。
    在Java中,用同步代码块可以解决这个问题,格式为:
    synchronized(对象)
    {
        需要被同步的代码;
    }

    同步的好处:解决了线程的安全问题;
    同步的弊端:相对降低了效率,因为同步外的线程都会判断同步锁。
    同步的前提:同步中必须有多个线程,并使用同一个锁。
    线程安全问题示例、同步代码块:

    class Ticket implements Runnable
    {
        private int num=20;
        Object obj=new Object();
        public void run()
        {
            sale();
        }
        public void sale()
        {
            while (true)
            {
                synchronized(obj)//如果在此处使用 new Objcet(),则使用的不是同一个锁
                {
                    if (num>0)
                    {
                        System.out.println(Thread.currentThread().getName()+":"+num--);
                    }
                }
            }
        }
    }
    
    class  TicketDemo
    {
        public static void main(String[] args) 
        {
            Ticket t=new Ticket();//创建一个线程任务对象
            Thread t1=new Thread(t);
            Thread t2=new Thread(t);
            Thread t3=new Thread(t);
            Thread t4=new Thread(t);
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
    

    同步函数使用的锁是this
    同步函数示例:

    class Ticket implements Runnable
    {
        private int num=100;
        //Object obj=new Object();
        boolean flag=true;
        public void run()
        {
            sale();
        }
        public  void sale()
        {
            if(flag)
                while (true)
                {
                    synchronized(this)
                    {
                        if (num>0)
                        {
                            System.out.println(Thread.currentThread().getName()+":"+num--);
                        }
                    }
                }
            else
                while(true)
                    show();
        }
        public synchronized void show()
        {
            if (num>0)
                    {
                        System.out.println(Thread.currentThread().getName()+" function :"+num--);
                    }
        }
    }
    
    class  SynFunctionLockDemo
    {
        public static void main(String[] args) 
        {
            Ticket t=new Ticket();//创建一个线程任务对象
            Thread t1=new Thread(t);
            Thread t2=new Thread(t);
            Thread t3=new Thread(t);
            Thread t4=new Thread(t);
            t1.start();
            try
            {
                Thread.sleep(10);
            }
            catch (InterruptedException e)
            {
            }
            t.flag=false;
            t2.start();
        }
    }
    
    

    同步函数和同步代码块的区别:
    同步函数的锁是固定的this;
    同步代码块的锁是任意的对象。
    建议使用同步代码块。
    静态的同步函数使用的锁是,该函数所属的字节码文件对象,可以用getClass() 方法获取,也可以用 当前 类名.class()获取
    Class cla=t.getClass();
    Class cla=Ticket.class;
    静态同步函数锁示例:

    class Ticket implements Runnable
    {
        private static int num=100;
        boolean flag=true;
        public void run()
        {
            sale();
        }
        public  void sale()
        {
            if(flag)
                while (true)
                {
                    synchronized(Ticket.class)
                    {
                        if (num>0)
                        {
                            try
                            {
                                Thread.sleep(10);
                            }
                            catch (InterruptedException e)
                            {
                            }
                            System.out.println(Thread.currentThread().getName()+":"+num--);
                        }
                    }
                }
            else
                while(true)
                    show();
        }
        public static synchronized void show()
        {
            if (num>0)
                    {
                        System.out.println(Thread.currentThread().getName()+" function :"+num--);
                    }
        }
    }
    
    class  SynFunctionLockDemo
    {
        public static void main(String[] args) 
        {
            Ticket t=new Ticket();//创建一个线程任务对象
            Thread t1=new Thread(t);
            Thread t2=new Thread(t);
            Thread t3=new Thread(t);
            Thread t4=new Thread(t);
            t1.start();
            try
            {
                Thread.sleep(10);
            }
            catch (InterruptedException e)
            {
            }
            t.flag=false;
            t2.start();
        }
    }
    
    

    多线程下的单例示例:

    //饿汉式(单例设计模式)
    class Single//类一加载,对象就已经存在
    {
        private static Single s=new Single();//在本类中创建对象
        private Single(){};//私有化构造函数
        public static Single getInstance()//公有方法,返回对象
        {
            //多线程中不存在安全隐患
            return s;
        }
    }
    
    //懒汉式(延迟加载单例设计模式)
    class Single2//类加载进来,没有对象;只有调用了getInstance时,才会创建对象
                        //延迟加载形式
    {
        private static Single2 s=null;
        private Single2(){};
        public static Single2 getInstance()
        {
            if (s==null)//加入此行代码,当s不为空时,则不再判断锁,提高效率
            {
                synchronized(Single.class)//静态函数,不能使用getClass()方法
                {
                    if(s==null)
                        // -->0   -->1  线程0,1可能存在安全隐患,需要同步锁
    					/*
    					 *线程0经过判断语句进入,然后执行权切换;
    					 *线程1经过判断语句进入,执行权切换;
    					 *执行权切换回0,new一个对象,并返回;
    					 *执行权切换到2,由于已经经过判断,所以此处不需要再次判断,new一个对象,返回;
    					 *造成的后果是有了2个对象,不能保证单例。
    					 */
                        s=new Single2();
                }
            }
            return s;
        }
    }
    
    class SingleDemo 
    {
        public static void main(String[] args) 
        {
            Single s1= Single.getInstance();
            Single s2= Single.getInstance();
            System.out.println(s1==s2);
        }
    }
    
    

    死锁示例:

    class Ticket implements Runnable
    {
        private static int num=100;
        boolean flag=true;
        public void run()
        {
            sale();
        }
        public  void sale()
        {
            if(flag)
                while (true)
                {
                    synchronized(Ticket.class)
                    {
                        if (num>0)
                        {
                            try
                            {
                                Thread.sleep(10);
                            }
                            catch (InterruptedException e)
                            {
                            }
                            System.out.println(Thread.currentThread().getName()+":"+num--);
                        }
                    }
                }
            else
                while(true)
                    show();//此处线程0获得锁this
        }
        public static synchronized void show()
        {
            if (num>0)
                    {
                        System.out.println(Thread.currentThread().getName()+" function :"+num--);
                    }
        }
    }
    
    class  SynFunctionLockDemo
    {
        public static void main(String[] args) 
        {
            Ticket t=new Ticket();//创建一个线程任务对象
            Thread t1=new Thread(t);
            Thread t2=new Thread(t);
            Thread t3=new Thread(t);
            Thread t4=new Thread(t);
            t1.start();
            try
            {
                Thread.sleep(10);
            }
            catch (InterruptedException e)
            {
            }
            t.flag=false;
            t2.start();
        }
    }
    
    

    死锁示例:

    class Test implements Runnable
    {
        private boolean flag;
        Test(boolean flag)
        {
            this.flag=flag;
        }
        public void run()
        {
            if(flag)
            {
                synchronized(MyLock.locka)
                {//1、线程0获得锁MyLock.locka,继续下一句;尝试获得MyLock.lockb,此时线程1占有MyLock.lockb,无法获得
                    System.out.println(Thread.currentThread().getName()+".....if......locka");
                    synchronized(MyLock.lockb)
                    {
                        System.out.println(Thread.currentThread().getName()+".....if.....lockb");
                    }
                }
            }
            else
            {
                synchronized(MyLock.lockb)
                {//2、线程1获得锁MyLock.lockb,继续下一句;尝试获得MyLock.locka,此时线程0占有MyLock.locka,无法获得
                    System.out.println(Thread.currentThread().getName()+".....else.....lockb");
                    synchronized(MyLock.locka)
                    {
                        System.out.println(Thread.currentThread().getName()+".....else.....locka");
                    }
                }
            }
        }
    }
    
    class MyLock
    {
        public static final Object locka=new Object();
        public static final Object lockb=new Object();
    
    }
    
    class DeadLockTest 
    {
        public static void main(String[] args) 
        {
            Test a=new Test(true);
            Test b=new Test(false);
            Thread t1=new Thread(a);
            Thread t2=new Thread(b);
            t1.start();
            t2.start();
        }
    
    }
    
    

    1、线程0获得锁MyLock.locka,继续下一句;尝试获得MyLock.lockb,此时线程1占有MyLock.lockb,无法获得;
    2、线程1获得锁MyLock.lockb,继续下一句;尝试获得MyLock.locka,此时线程0占有MyLock.locka,无法获得;
    此时,就可能发生死锁。

    线程间通信

    /*
    线程间通信:
    多个线程在处理同一资源,但是任务却不同。
    */
    //资源
    class Resource
    {
        String name;
        String sex;
    }
    
    //输入
    class Input implements Runnable
    {
        Resource r;
        Object obj=new Object();
        Input(Resource r)
        {
            this.r=r;
        }
        public void run()
        {
            int x=0;
            while (true)
            {
                synchronized(r)
                {
                    if(x==0)
                    {
                        r.name="mike";
                        r.sex="male";
                    }
                    else
                    {
                        r.name="lili";
                        r.sex="female";
                    }
                }
                x=(x+1)%2;
            }
        }
    }
    
    //输出
    class Output implements Runnable
    {
        Resource r;
        Output(Resource r)
        {
            this.r=r;
        }
        public void run()
        {    
            while(true)
            {
                synchronized(r)
                {
                    System.out.println(r.name+"......."+r.sex);
                }
            }
        }
    }
    
    class  ResourceDemo
    {
        public static void main(String[] args) 
        {
            Resource r=new Resource();
            Input in=new Input(r);
            Output out=new Output(r);
            Thread t1=new Thread(in);
            Thread t2=new Thread(out);
            t1.start();
            t2.start();
        }
    }
    
    

    等待/唤醒机制
    涉及的方法:
    1.wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
    2.notify():唤醒线程池中的一个线程(任意)。
    3.notifyAll():唤醒线程池中的所有线程。

    这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法,
    必须要明确到底操作的是哪个锁上的线程。

    为什么操作线程的方法 wait、notify、notifyAll定义在了Object类中?
    因为这些方法是监视器的方法,监视器其实就是锁。
    锁可以是任意的对象,任意的对象调用的方法一定定义在Object类中。

    等待唤醒机制示例:

    //资源
    class Resource
    {
        private String name;
        private String sex;
        private boolean flag=false;
        public synchronized void set(String name,String sex)
        {
            if (flag)
            {
                try
                {
                    wait();
                }
                catch (InterruptedException e)
                {
                }
            }
            this.name=name;
            this.sex=sex;
            flag=true;
            notify();
        }
        public synchronized void get()
        {
            if(!flag)
                try
                {
                    wait();
                }
                catch (InterruptedException e)
                {
                }
            System.out.println(name+"..............."+sex);
            flag=false;
            notify();
        }
    }
    
    //输入
    class Input implements Runnable
    {
        Resource r;
        Object obj=new Object();
        Input(Resource r)
        {
            this.r=r;
        }
        public void run()
        {
            int x=0;
            while (true)
            {                    
                    if(x==0)
                    {
                        r.set("mike","male");
                    }
                    else
                    {
                        r.set("lili","female");
                    }
                x=(x+1)%2;
            }
        }
    }
    
    //输出
    class Output implements Runnable
    {
        Resource r;
        Output(Resource r)
        {
            this.r=r;
        }
        public void run()
        {    
            while(true)
            {
                r.get();
            }
        }
    }
    
    class  ResourceDemo3
    {
        public static void main(String[] args) 
        {
            Resource r=new Resource();
            Input in=new Input(r);
            Output out=new Output(r);
            Thread t1=new Thread(in);
            Thread t2=new Thread(out);
            t1.start();
            t2.start();
        }
    }
    
    

    if判断标记只有一次,会导致不该运行的线程运行了,出现了数据错误的情况。
    while判断标记,解决了线程获取执行权后,是否要运行的问题。
    notify只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+nitify会导致死锁。
    notifyAll解决了本方线程一定会唤醒对方线程的问题。
    多生产者-多消费者示例:

    class Resource
    {
        private String name;
        private int count=1;
        private boolean flag=false;
        public synchronized void set(String name)
        {
            while(flag)
                try{wait();}catch(InterruptedException e){}
            this.name=name+count;
            count++;
            System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
            flag=true;
            notifyAll();
        }
        public synchronized void get()
        {
            while(!flag)
            try{wait();}catch(InterruptedException e){}
            System.out.println(Thread.currentThread().getName()+"...消费者......"+this.name);
            flag=false;
            notifyAll();
        }
    }
    
    class Producer implements Runnable
    {
        private Resource r;
        Producer(Resource r)
        {
            this.r=r;
        }
        public void run()
        {
            while (true)
            {
                r.set("烤鸭");
            }
        }
    }
    class Consumer implements Runnable
    {
        private Resource r;
        Consumer(Resource r)
        {
            this.r=r;
        }
        public void run()
        {
            while (true)
            {
                r.get();
            }
        }
    }
    
    class  ProducerConsumerDemo
    {
        public static void main(String[] args) 
        {
            Resource r=new Resource();
            Producer pro=new Producer(r);
            Consumer con=new Consumer(r);
            Thread t0=new Thread(pro);
            Thread t1=new Thread(con);
            Thread t2=new Thread(pro);
            Thread t3=new Thread(con);
            t0.start();
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    

    接口 Lock
    同步代码块,对于锁的操作时隐式的
    jdk1.5以后,将同步和锁封装成了对象,并将操作锁的方式定义到了该对象中,将隐式动作变成了显式动作。
    Lock lock=new ReentrantLock();
    lock.lock();//获取锁
    code...;//throw Exception();
    lock.unlock();//释放锁
    try
    {
        lock.lock();//获取锁
    }
    finally
    {
        lock.unlock;//释放锁
    }

    Lock接口:它的出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成了显式锁操作;
    同时更为灵活,可以一个锁上加上多组监视器。
        lock();获取锁
        unlock();释放锁,通常需要定义到finally代码块中。

    Condition接口:它的出现替代了Object中的wait、notify、notifyAll方法。
                    将这些监视器方法单独进行了封装,变成了Condition监视器对象,
                    可以和任意锁组合。
        await();//等待
        signal();//唤醒一个等待线程
        signalAll();//唤醒所有等待线程

    接口Lock应用示例:

    import java.util.concurrent.locks.*;
    class Resource
    {
        private String name;
        private int count=1;
        private boolean flag=false;
        //创建一个锁对象
        Lock lock=new ReentrantLock();
        //通过已有的锁,获取该锁上的监视器对象
        //Condition con=lock.newCondition();
        //通过已有的锁,获取两组监视器;一组监视生产者,一组监视消费者
        Condition producer_con=lock.newCondition();
        Condition consumer_con=lock.newCondition();
        public void set(String name)
        {
            lock.lock();
            try
            {
                while(flag)
                try{producer_con.await();}catch(InterruptedException e){}
                this.name=name+count;
                count++;
                System.out.println(Thread.currentThread().getName()+"...生产者5.0..."+this.name);
                flag=true;
                consumer_con.signal();
            }
            finally
            {
                lock.unlock();
            }
    
        }
        public void get()
        {
            lock.lock();
            try
            {
                while(!flag)
                try{consumer_con.await();}catch(InterruptedException e){}
                System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
                flag=false;
                producer_con.signal();
            }
            finally
            {
                lock.unlock();
            }
            
        }
    }
    
    class Producer implements Runnable
    {
        private Resource r;
        Producer(Resource r)
        {
            this.r=r;
        }
        public void run()
        {
            while (true)
            {
                r.set("烤鸭");
            }
        }
    }
    class Consumer implements Runnable
    {
        private Resource r;
        Consumer(Resource r)
        {
            this.r=r;
        }
        public void run()
        {
            while (true)
            {
                r.get();
            }
        }
    }
    
    class  ProducerConsumerDemo
    {
        public static void main(String[] args) 
        {
            Resource r=new Resource();
            Producer pro=new Producer(r);
            Consumer con=new Consumer(r);
            Thread t0=new Thread(pro);
            Thread t1=new Thread(con);
            Thread t2=new Thread(pro);
            Thread t3=new Thread(con);
            t0.start();
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    

    wait和sleep的区别
    1.wait可以指定时间,也可以不指定;
         sleep必须指定时间。
    2.在同步中时,对于cpu的执行权和锁的处理不同。
         wait释放执行权,释放锁。
         sleep释放执行权,不释放锁。

    停止线程
    1.stop方法(已过时);
    2.run方法结束;
        怎么控制线程的任务结束?
        任务中都会有循环结构,只要控制住循环就可以结束任务。
        控制循环:通常用定义标记来完成。

    如果线程处于冻结状态,无法读取标记,如何结束?
    可以使用interrupt()方法,将线程从冻结状态强制恢复到运行状态中来,让线程具备
    cpu的执行资格。
    但是强制动作会发生InterruptedException,需要处理。

    class StopThread implements Runnable
    {
        private boolean flag=true;
        public synchronized void run()
        {
            while (flag)
            {
                try
                {
                    wait();
                }
                catch (InterruptedException e)
                {
                    System.out.println(Thread.currentThread().getName()+",,,,,,,"+e);
                    flag=false;
                }
                    System.out.println(Thread.currentThread().getName()+"......");
            }
        }
        public void setFlag()
        {
            flag=false;
        }
    }
    
    class  StopThreadDemo
    {
        public static void main(String[] args) 
        {
            StopThread st=new StopThread();
            Thread t1=new Thread(st);
            Thread t2=new Thread(st);
            t1.start();
            t2.setDaemon(true);//守护线程(后台线程)
            t2.start();
            int num=1;
            for (;;)
            {
                if(++num==50)
                {
                    //st.setFlag();
                    t1.interrupt();
                    //t2.interrupt();
                    break;
                }
                System.out.println("main"+num);
            }
            System.out.println("over");
        }
    }
  • 相关阅读:
    SpringBoot相知
    SpringBoot初识
    Mybatis 3 配置 Log4j
    IdeaVim-常用操作
    [ZZ] 基于Matlab的标记分水岭分割算法
    [综] meanshift算法
    [ZZ] 麻省理工( MIT)大神解说数学体系
    [ZZ] UIUC同学Jia-Bin Huang收集的计算机视觉代码合集
    公务员 素养
    [zz]有哪些优秀的科学网站和科研软件推荐给研究生?
  • 原文地址:https://www.cnblogs.com/chenchong/p/2608802.html
Copyright © 2020-2023  润新知