zoukankan      html  css  js  c++  java
  • 多线程知识点:同步

    /*
    两个储户,到同一个银行存钱,每个人存了3次,一次100元。
    1,描述银行。
    2,描述储户任务。
    
    分析多线程是否存在安全隐患。
    
    1,线程任务中是否有共享的数据。
    2,是否多条操作共享数据的代码。
    
    
    
    同步函数。其实就是在函数上加上了同步关键字进行修饰。
    同步表现形式有两种:1,同步代码块,2,同步函数。
    
    同步函数使用的锁是什么呢?函数需要被对象调用,哪个对象不确定,但是都用this来表示。
    同步函数使用的锁就是this。
    
    */
    class Bank
    {
        private int sum;
    //    private Object obj = new Object();
        public synchronized  void add(int n)
        {
    //        synchronized(obj)
    //        {
            sum = sum + n;
            try{Thread.sleep(10);}catch(Exception e){}
            System.out.println("sum="+sum);
    //        }
            
        }
    }
    class Customer implements Runnable
    {
        private    Bank b = new Bank();
        public void run()
        {
            for(int x=0; x<3; x++)
            {
                b.add(100);
            }
        }
    }
    
    class BankDemo
    {
        public static void main(String[] args) 
        {
            //1,创建任务对象。
            Customer c = new Customer();
            Thread t1 = new Thread(c);
            Thread t2 = new Thread(c);
            t1.start();
            t2.start();
        }
    }
    /*
    验证同步函数使用的锁是this。
    
    验证需求:
    启动两个线程。
    一个线程负责执行同步代码块(使用明锁)。
    另一个线程使用同步函数(使用this锁)。
    两个执行的任务是一样的都是卖票。如果他们没有使用相同的锁,说明他们没有同步。会出现数据错误。
    
    怎么能让一个线程一直在同步代码块中,一个线程在同步函数呢?
    可以通过切换的方式。
    */
    
    class SaleTicket implements Runnable
    {
        private int tickets = 100;
        //定义一个boolean标记。
        boolean flag = true;
        Object obj = new Object();
        public void run()
        {
            if(flag)        
                while(true)
                {
                    synchronized(this)
                    {
                        if(tickets>0)
                        {    
                            try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
                            System.out.println(Thread.currentThread().getName()+"....code....."+tickets--);
                        }
                    }
                }
            else
                while(true)
                    sale();
        }
    
        public synchronized void sale()
        {
            if(tickets>0)
            {    
                try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
                System.out.println(Thread.currentThread().getName()+".....func...."+tickets--);
            }
        }
    }
    
    
    
    class ThisLockDemo 
    {
        public static void main(String[] args) throws InterruptedException
        {
            SaleTicket t = new SaleTicket();
    
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            t1.start();
            Thread.sleep(10);
            t.flag = false;
            t2.start();
        }
    }
    /*
    如果同步函数被static修饰呢?
    
    static方法随着类加载,这时不一定有该类的对象。但是一定有一个该类的字节码文件对象。
    这个对象简单的表示方式就是 类名.class  Class
    
    
    
    */
    
    class SaleTicket implements Runnable
    {
        private static int tickets = 100;
        //定义一个boolean标记。
        boolean flag = true;
        Object obj = new Object();
        public void run()
        {
            if(flag)        
                while(true)
                {
                    synchronized(SaleTicket.class)
                    {
                        if(tickets>0)
                        {    
                            try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
                            System.out.println(Thread.currentThread().getName()+"....code....."+tickets--);
                        }
                    }
                }
            else
                while(true)
                    sale();
        }
    
        public static  synchronized void sale()
        {
            if(tickets>0)
            {    
                try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
                System.out.println(Thread.currentThread().getName()+".....func...."+tickets--);
            }
        }
    }
    
    
    
    class StaticLockDemo 
    {
        public static void main(String[] args) throws InterruptedException
        {
            SaleTicket t = new SaleTicket();
    
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            t1.start();
            Thread.sleep(10);
            t.flag = false;
            t2.start();
        }
    }
    //饿汉式。相对与多线程并发,安全!
    
    class Single
    {
        private static final Single SINGLE_INTSTANCE = new Single();
        private Single(){}
        public static Single getInstance()
        {
            return SINGLE_INTSTANCE;
        }
    }
    
    //懒汉式。延迟加载模式。
    /*
    在多线程并发访问时,会出现线程安全问题。
    加了同步就可以解决问题。无论是同步函数,还是同步代码块都行。
    但是,效率低了。
    怎么解决效率低的问题。
    可以通过if对单例对象的双重判断的形式。哦耶!
    
    
    */
    class Single
    {
        private static Single s = null;
        private Single(){}
        public static  Single getInstance()
        {
            if(s==null)
            {
                synchronized(Single.class)
                {
                    if(s==null)
                        //-->0 
                        s = new Single();
                }
            }
            return s;
        }
    }
    
    class Demo implements Runnable
    {
        public void run()
        {
            Single.getInstance();
        }
    }
    
    
    class ThreadSingleTest 
    {
        public static void main(String[] args) 
        {
            System.out.println("Hello World!");
        }
    }
    /*
    同步函数,同步代码块。
    
    同步代码块使用的任意的对象作为锁。
    同步函数只能使用this作为锁。
    
    
    如果说:一个类中只需要一个锁,这时可以考虑同步函数,使用this,写法简单。
    但是,一个类中如果需要多个锁,还有多个类中使用同一个锁,这时只能使用同步代码块。
    
    建议使用同步代码块。
    
    */
    
    class  
    {
        public static void main(String[] args) 
        {
            System.out.println("Hello World!");
        }
    }
    /*
    死锁:
    
    场景一:
    
    同步嵌套。
    
    
    
    */
    
    class SaleTicket implements Runnable
    {
        private int tickets = 100;
        //定义一个boolean标记。
        boolean flag = true;
        Object obj = new Object();
        public void run()
        {
            if(flag)        
                while(true)
                {
                    synchronized(obj)//obj lock
                    {
                        sale();
                    }
                }
            else
                while(true)
                    sale();
        }
    
        public synchronized void sale()//this lock
        {
            synchronized(obj)
            {
                if(tickets>0)
                {    
                    try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。
                    System.out.println(Thread.currentThread().getName()+".....func...."+tickets--);
                }
            }
        }
    }
    
    
    
    class DeadLockDemo 
    {
        public static void main(String[] args) throws InterruptedException
        {
            SaleTicket t = new SaleTicket();
    
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            t1.start();
            Thread.sleep(10);
            t.flag = false;
            t2.start();
        }
    }
    /*
    多线程间的通信。多个线程都在处理同一个资源,但是处理的任务却不一样。
    生产者,消费者。
    
    通过同步,解决了没生产就消费的问题。
    
    
    */
    //描述资源。
    class Res
    {
        private String name;
        private int count = 1;
    
        //提供了给商品赋值的方法。
        public synchronized void set(String name)
        {
            this.name  = name + "--" + count;
    
            count++;
    
            System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name);
        }
    
        //提供一个获取商品的方法。
        public synchronized void get()
        {
            System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name);
        }
    }
    
    
    //生成者。
    class Producer implements Runnable
    {
        private Res r;
        Producer(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            while(true)
                r.set("面包");
        }
    }
    
    //消费者
    class Consumer implements Runnable
    {
        private Res r;
        Consumer(Res r)
        {
            this.r = r;
        }
        public void run()
        {    
            while(true)
            r.get();
        }
    }
    
    
    
    
    class  ProducerConsumerDemo
    {
        public static void main(String[] args) 
        {
            //1,创建资源。
            Res r = new Res();
            //2,创建两个任务。
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);
    
            //3,创建线程。
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(con);
    
            t1.start();
            t2.start();
        }
    }

    //同步嵌套,死锁。
    
    class Task implements Runnable
    {
        private boolean flag;
        Task(boolean flag)
        {
            this.flag = flag;
        }
        public void run()
        {
            if(flag)
            {
                while(true)
                    synchronized(MyLock.LOCKA)
                    {
                        System.out.println("if.....locka");
                        synchronized(MyLock.LOCKB)
                        {
                            System.out.println("if.....lockb");
                        }
                    }
            }
            else
            {
                while(true)
                    synchronized(MyLock.LOCKB)
                    {
                        System.out.println("else.....lockb");
                        synchronized(MyLock.LOCKA)
                        {
                            System.out.println("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) 
        {
            //创建线程任务。
            Task t1 = new Task(true);
            Task t2 = new Task(false);
            new Thread(t1).start();
            new Thread(t2).start();
        }
    }
    /*
    多线程间的通信。多个线程都在处理同一个资源,但是处理的任务却不一样。
    生产者,消费者。
    
    通过同步,解决了没生产就消费的问题。
    
    但是出现了连续的生产没有消费的情况,和需求生产一个,消费一个的情况不符。
    
    使用了等待唤醒机制。
    wait():该方法可以让线程处于冻结状态,并将线程临时存储到线程池中。
    notify():唤醒指定线程池中的任意一个线程。
    notifyAll():唤醒指定线程池中的所以线程。
    
    
    
    这些方法必须使用在同步中,因为它们用来操作同步锁上的线程的状态的。
    
    在使用这些方法时,必须标识它们所属于的锁。标识方式就是 锁对象.wait();  锁对象.notify();  锁对象.notifyAll();
    
    相同锁的notify(),可以获取相同锁的wait();
    
    
    
    */
    //描述资源。
    class Res
    {
        private String name;
        private int count = 1;
    
        //定义标记。
        private boolean flag;
        //提供了给商品赋值的方法。
        public synchronized void set(String name)
        {
            if(flag)//判断标记为true,执行wait。等待。为false。就生产。
                try{this.wait();}catch(InterruptedException e){}
            this.name  = name + "--" + count;
    
            count++;
    
            System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name);
            //生成完毕,将标记改为true。
            flag = true;
    
            //唤醒消费者。
            this.notify();
        }
    
        //提供一个获取商品的方法。
        public synchronized void get()
        {
            if(!flag)
                try{this.wait();}catch(InterruptedException e){}
            System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name);
    
            //将标记改为false。
            flag = false;
            //唤醒生产者。
            this.notify();
        }
    }
    
    
    //生成者。
    class Producer implements Runnable
    {
        private Res r;
        Producer(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            while(true)
                r.set("面包");
        }
    }
    
    //消费者
    class Consumer implements Runnable
    {
        private Res r;
        Consumer(Res r)
        {
            this.r = r;
        }
        public void run()
        {    
            while(true)
                r.get();
        }
    }
    
    
    
    
    class  ProducerConsumerDemo2
    {
        public static void main(String[] args) 
        {
            //1,创建资源。
            Res r = new Res();
            //2,创建两个任务。
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);
    
            //3,创建线程。
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(con);
    
            t1.start();
            t2.start();
        }
    }
    /*
    
    多生产多消费。
    
    问题1:
        重复生成,重复消费。
        原因:经过复杂的(等,资格)分析,发现被唤醒的线程没有判断标记就开始工作(生成or消费)了。
        导致了重复的生成和消费的发生。
    
        解决:那就是被唤醒的线程必须判断标记。
            使用while循环搞定。
    
    
    
    问题2:
        死锁了。所有的线程都处于冻结状态。
        原因:本方线程在唤醒时,又一次唤醒了本方线程。而本方线程循环判断标记,又继续等待。
        而导致所有的线程都等待了。
    
        解决;希望本方如果唤醒了对方线程,就可以解决。
            可以使用notifyAll()方法。
            那不是全唤醒了吗?是的。既有本方,又有对方。但是本方醒后,会判断标记继续等待。
            这样对方就有线程可以执行了。
    
    
    
    已经实现了多生产多消费。
    
    但是有些小问题,效率有点低,因为notifyAll也唤醒了本方。做了不必要的判断。
    */
    
    
    //描述资源。
    class Res
    {
        private String name;
        private int count = 1;
    
        //定义标记。
        private boolean flag;
        //提供了给商品赋值的方法。
        public synchronized void set(String name)//
        {
            while(flag)//判断标记为true,执行wait。等待。为false。就生产。
                try{this.wait();}catch(InterruptedException e){}//t0(等)  t1(等)
            this.name  = name + "--" + count;//面包1  ,面包2 面包3
    
            count++;//2  3 4
    
            System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name);//t0  面包1、 t0 面包2 t1 ,面包3
            //生成完毕,将标记改为true。
            flag = true;
    
            //唤醒所有等待线程(包括本方线程)。
            this.notifyAll();
        }
    
        //提供一个获取商品的方法。
        public synchronized void get()//
        {
            while(!flag)
                try{this.wait();}catch(InterruptedException e){}//t2(等)  t3(等)
            System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name);//t2  面包1.
    
            //将标记改为false。
            flag = false;
            //唤醒所有等待线程(包括本方线程)。
            this.notifyAll();
        }
    }
    
    
    //生成者。
    class Producer implements Runnable
    {
        private Res r;
        Producer(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            while(true)
                r.set("面包");
        }
    }
    
    //消费者
    class Consumer implements Runnable
    {
        private Res r;
        Consumer(Res r)
        {
            this.r = r;
        }
        public void run()
        {    
            while(true)
                r.get();
        }
    }
    
    
    
    
    class  ProducerConsumerDemo3
    {
        public static void main(String[] args) 
        {
            //1,创建资源。
            Res r = new Res();
            //2,创建两个任务。
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);
    
            //3,创建线程。
            Thread t0 = new Thread(pro);
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(con);
            Thread t3 = new Thread(con);
    
            t0.start();
            t1.start();
            t2.start();
            t3.start();
        }
    }
    /*
    
    1,搞定妖的问题。
    
    
    2,name和sex是私有的。需要在Res类中对外提供访问name和sex的方法。这个可以参照生产者消费者producerconsumerdemo.java
    
    
    3,实现间隔输出,使用等待唤醒机制。producerconsumerdemo2.java
    
    
    
    */
    
    class Res
    {
        String name;
        String sex;
    }
    
    class Input implements Runnable
    {
    
        private Res r;
        Input(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            int x = 0;
            while(true)
            {
                if(x==0)
                {
                    r.name = "张三";
                    r.sex = "男男男男男男男";
                }
                else
                {
                    r.name = "rose";
                    r.sex = "women";
                }
                x = (x+1)%2;
            }
        
        }
    
    }
    
    class Output implements Runnable
    {
    
        private Res r;
        Output(Res r)
        {
            this.r = r;
        }
        public void run()
        {
            while(true)
            {
                System.out.println(r.name+"...."+r.sex);
            }
        }
    
    }
    
    
    class Test 
    {
        public static void main(String[] args) 
        {
            Res r = new Res();
    
            Input in = new Input(r);
            Output out = new Output(r);
    
            new Thread(in).start();
            new Thread(out).start();
        
        
        }
    }
  • 相关阅读:
    第五章 kubernetes常见故障排错
    第二十五章 Centos7下二进制安装Mysql-5.7.34
    第二十四章 MySQL导入数据常见报错解决
    第二十三章 Centos7 下 Mysql 8.0.24编译安装
    第二十二章 Centos7下 Mysql 8.0.24 二进制安装
    第二十一章 MySQL数据库优化
    Sqoop导入数据到mysql数据库报错:ERROR tool.ExportTool: Error during export: Export job failed!
    python读取csv、excel、mysql内容
    WTF表单验证
    Flask_WTF实现表单
  • 原文地址:https://www.cnblogs.com/vijay/p/3511261.html
Copyright © 2011-2022 走看看