美文网首页
Java多线程---同步

Java多线程---同步

作者: 最困惑的时候就是能成长的时候 | 来源:发表于2019-03-09 14:01 被阅读0次

    同步:就是开启多线程的时候,如果需要对同一个对象进行操作,这个时候可能会同时对其进行修改,那么需要先把这个对象进行锁定,然后进行操作,这个过程就是同步。
    首先,我们想给一个int对象+1,总共加5次,初始化为0,每次输出加 1 后的值。那么我们如果在单线程的方式中我们直接一个for循环,从1加到5,即可实现。如果使用多线程不加锁的情况下,可能产生问题

    public class MainThread {
        static int index=0;
        public static void main(String[] args) {
            for(int i=0;i<5;i++){
                new Thread(new Runnable() {
                    public void run() {
                        index+=1;
                        System.out.println(index);
                    }
                }).start();
            }
        }
    }
    

    结果可能出现错误:


    加锁前的结果

    1.Synchronized

    仅仅使用synchronized进行锁定。

    A: 锁定对象

    public class MainThread {
        static int index = 0;
    
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                new Thread(new Runnable() {
                    public void run() {
                       synchronized (MyConstants.lock) { //这里加锁之后只会去先去获得所锁的所有权,不然一直处于等待状态
                            index += 1;
                            System.out.println(index);
                        }
                    }
                }).start();
            }
        }
    }
    
    加锁后的结果

    B.锁定方法

    例如:你新建了一个老师对象,有一个方法用于看学生的试卷,假设学生在任意时间都有可能在交试卷,而老师只能一次看一个,所以一个批改试卷的方法只能同步执行,如果改了一个试卷就给老师改试卷的数加1

    public class Teacher {
        private int checkPaperNumber=0;
    
        public void checkStudentPaper(){ //没有加synchronized关键字
            checkPaperNumber+=1;
            System.out.println(checkPaperNumber);
        }
    }
    
    public class MainThread {
        static Teacher teacherWang = new Teacher();
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {    //开启10个线程
                new Thread(new Runnable() {
                    public void run() {
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        teacherWang.checkStudentPaper(); //执行老师批卷子的方法
                    }
                }).start();
            }
        }
    }
    

    可能出现的结果


    结果

    添加synchronized后:

     public synchronized void checkStudentPaper(){
    
    image.png

    C:与wait()和nitify()方法使用

    假设有一个仓库WAREHOUSE,容积为10,现在有一个Producer,生产过程需要200ms,每次生产一个,放到仓库,有一个Consumer赶到仓库需要200ms,每次消费一个。现在假设有
    生产线程和10个消费线程。只有一个仓库,约定:只要阻塞线程大于5,唤醒所有阻塞线程,小于5唤醒一个阻塞线程

    public class MyConstants {
        public static WareHouse wareHouse = new WareHouse(10); //定义的共有仓库
        public static int thread_wait_number = 0;  //现在阻塞线程数
    

    Producer.java

    public class Producer extends Thread{
        private String name;
        public Producer(String name){
            this.name = name;
        }
        @Override
        public void run() {
            super.run();
            while(true){
                try {
                    Thread.sleep(200);   //生产过程与仓库无关
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (wareHouse){   //需要向仓库记账就锁定仓库,拿出账本完成记账过程
                    if(wareHouse.getCubage() <10){  //如果仓库有空余的地方那么就直接放入仓库并查看阻塞线线程数
                        wareHouse.produceGoods();
                        System.out.println("Producer Name"+name+":"+wareHouse.getCubage());
                        if(wareHouse.getCubage()<10 && wareHouse.getCubage()>0 ){
                            if( MyConstants.thread_wait_number >5) {  //阻塞线程大于5时,就可以唤醒所有线程
                                System.out.println("消费者"+name+"唤醒所有阻塞线程");
                                wareHouse.notifyAll();  
                                thread_wait_number=0;
                            }else if(MyConstants.thread_wait_number>0){  //小于5时就唤醒一个线程
                                System.out.println("消费者"+name+"唤醒单个阻塞线程");
                                wareHouse.notify();
                                thread_wait_number-=1;
                            }
                        }
                    }else{
                        try {
                            MyConstants.thread_wait_number +=1;
                            System.out.println("Producer Name"+name+":"+"阻塞中...");
                            wareHouse.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    

    Consumer.java

    public class Consumer extends Thread{
        private String name;
    
        public Consumer(String name) {
            this.name = name;
        }
    
        @Override
        public void run() {
            super.run();
            while(true){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (wareHouse){
                    if(wareHouse.getCubage() >0){
                        wareHouse.consumerGoods();
                        System.out.println("Consumer Name"+name+":"+wareHouse.getCubage());
                        if(wareHouse.getCubage()<10 && wareHouse.getCubage()>0 ){
                            if( MyConstants.thread_wait_number >5) {
                                System.out.println("消费者"+name+"唤醒所有阻塞线程");
                                wareHouse.notifyAll();
                            }else if(MyConstants.thread_wait_number>0){
                                System.out.println("消费者"+name+"唤醒单个阻塞线程。");
                                wareHouse.notify();
                            }
                        }
                    }else{
                        try {
                            MyConstants.thread_wait_number +=1;
                            System.out.println("Consumer Name"+name+":"+"阻塞中...");
                            wareHouse.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    

    Main函数

    public class MyThread {
        public static void main(String[] args) {
            for(int i=0;i<10;i++){
                Producer producer = new Producer(String.valueOf(i));
                producer.start();
            }
            for(int i=0;i<10;i++){
                Consumer consumer = new Consumer(String.valueOf(i));
                consumer.start();
            }
    
        }
    }
    

    运行结果:

    image.png

    总结:synchronized关键字是获取对象的锁,当自己代码所包裹的区域执行完毕可以释放对对象的控制权,而wait()方法是将对象的持有权放弃,并将本线程放入阻塞线程中。notify()方法是将想持有本对象的所有阻塞线程中的一个唤醒,notifyAll()是唤醒所有阻塞线程。光使用synchronized关键字是可以达到目的的,但是可能造成资源的浪费,长期霸占CPU运行时间,也可能因为持有其他对象导致其他线程无法正常运行。

    2.Volatile关键字

    一个变量int count=0,开启5个线程,每个线程都等待count的值不等于1的时候,另开启一个线程用于改变count的值

    public class VolatileThread {
        private static int count = 0;
        public static void main(String[] args) throws InterruptedException {
            for(int i=0;i<5;i++){
                new Thread(new Runnable() {
                    public void run() {
                        while(count == 0){   //监听count的值是否为
    
                        }
                        System.out.println(Thread.currentThread().getName()+"执行完毕");
                    }
                }).start();
            }
            Thread.sleep(200);
            new Thread(new Runnable() {    //用于改变count的值
                public void run() {
                    count +=1;
                }
            }).start();
        }
    }
    
    
    结果一直处于运行
    private volatile static int count = 0;                  //添加volatile关键字后
    
    image.png

    可使用的场景多个线程读,一个线程写。

    3.ReetrantLock解析

    1.简述

    1.方法lock()

    直接锁定声明的lock对象,如果锁对象可用就直接锁定,不可用进入等待,线程阻塞

    2.方法unlock()

    接触锁定的锁,没有锁定锁而进行的unlock会报错

    [图片上传失败...(image-6da169-1552810272527)]

    3.方法tryLock()

    获取锁的状态,如果锁可用直接锁定,不可用直接返回不等待,不用再进行锁定,tryLcok返回true后直接就已经锁定了锁对象

    4.方法tryLock(时间,时间单位)

    在给定的时间内去等待锁如果锁获取到就执行下面的东西,没有在给定时间内尝试获得锁,过了时间就直接返货false不在继续等待

    同样的同步的前提是锁定同样一个锁

    5.Condition

    1.Lock.newCondition

    创建一个condition

    2.Condition的阻塞和释放

    MyReentrantLock程序有两个方法,1.对count+1 2.对count-1

    约定如果加方法可以获取到锁,那么就判断是否现在的总数大于10, 1.大于10对addcondition进行阻塞。2.小于10,对总数加一并且释放一个减的程序,同样的减程序如果发现总数小于0,那么阻塞,否则减一并释放一个加程序

    public class MyReentrantLock {
    
        public void addMethod(){
            try{
                while(true) {
                    if (LockTest.lock.tryLock(100,TimeUnit.SECONDS)) {
                        TimeUnit.SECONDS.sleep(1);
                        if (LockTest.count >= 10) {
                            System.out.println(Thread.currentThread().getName()+":"+"加----功能阻塞");
                            LockTest.addcondition.await();
                        }else {
                            LockTest.count++;
                            if(LockTest.count>0){
                                System.out.println(Thread.currentThread().getName()+":"+"释放减功能");
                                LockTest.mincondition.signal();
                            }
                            System.out.println(Thread.currentThread().getName()+":"+LockTest.count);
                            LockTest.lock.unlock();
                        }
                    } else {
                        System.out.println("没获取到锁");
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public void minMethod(){
            try{
                while(true) {
                    if (LockTest.lock.tryLock(100,TimeUnit.SECONDS)) {
                        TimeUnit.SECONDS.sleep(1);
                        if (LockTest.count < 0) {
                            System.out.println(Thread.currentThread().getName()+":"+"减---开始阻塞!");
                            LockTest.mincondition.await();
                        }
                        else {
                            LockTest.count--;
                            if(LockTest.count<10) {
                                System.out.println(Thread.currentThread().getName()+":"+"释放加功能");
                                LockTest.addcondition.signal();
                            }
                            System.out.println(Thread.currentThread().getName()+":"+LockTest.count);
                            LockTest.lock.unlock();
                        }
                    } else {
                        System.out.println("没获取到锁");
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    

    我们开启5个线程用于加开启5个线程用于减

    public class LockTest {
     public static ReentrantLock lock = new ReentrantLock();
     public static Condition addcondition = lock.newCondition();
     public static Condition mincondition = lock.newCondition();
     public static int count=0;
     public static void main(String[] args) {
    ​
     for(int i=0;i<5;i++){
     new Thread(new Runnable() {
     @Override
     public void run() {
     MyReentrantLock myReentrantLock = new MyReentrantLock();
     myReentrantLock.addMethod();   //add
     }
     }).start();
     }
     for(int i=0;i<5;i++){
     new Thread(new Runnable() {
     @Override
     public void run() {
     MyReentrantLock myReentrantLock = new MyReentrantLock();
     myReentrantLock.minMethod();   //min
     }
     }).start();
     }
     }
    }
    

    2.ReentrantLock与synchronized关键字的区别

    1、增加了tryLock方法不至于一直使程序进入等待状态

    2、可实现公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序排队等待,而非公平锁则不保证这点,在锁释放时,任何一个等待锁的线程都有机会获得锁。synchronized中的锁时非公平锁,ReentrantLock默认情况下也是非公平锁,但可以通过构造方法ReentrantLock(ture)来要求使用公平锁。

    3、可以声明多个condition每个condition直接来源于lock的newCondition(),不用像synchronized去先使用synchronized锁定这个对象然后再执行wait方法

    synchronized (obj1){
     synchronized (obj2){
     if(condition1) {
     try {
     obj1.wait();
     } catch (InterruptedException e) {
     e.printStackTrace();
     }
     }else{
     if(condition2){
     try {
     obj2.wait();
     } catch (InterruptedException e) {
     e.printStackTrace();
     }
     }
     }
     }
     }
    

    相关文章

      网友评论

          本文标题:Java多线程---同步

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