zoukankan      html  css  js  c++  java
  • 解决线程安全-------Synchronized 同步代码块和同步方法

    1.背景
    例子:创建个窗口卖票,总票数为100张.使用实现Runnable接口的方式
    *
    * 1.问题:卖票过程中,出现了重票、错票 -->出现了线程的安全问题
    * 2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
    * 3.如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。

    2.Java解决方案:同步机制
    在Java中,我们通过同步机制,来解决线程的安全问题。

    方式一:同步代码块
    *
    * synchronized(同步监视器){
    * //需要被同步的代码/操作共享数据的代码
    *
    * }
    * 说明:1.操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。
    * 2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
    * 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。

      锁:是控制多个线程访问共享资源的一种方式,一般来说,一个锁能够防止多个线程同时访问共享资源。

    * 要求:多个线程必须要共用同一把锁。
    *
    * 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
    在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。

     实现runnable接口的代码中使用synchronized同步代码块
    package main.java.ThreadImplements;
    
    /**
     * @Author lx
     * @Description 实现runnable接口的代码中使用synchronized同步代码块
     * @Date 9:11 2020/8/5
     * @Version
     */
    class windowsSyn implements Runnable{
    
        private int ticket = 100;
        @Override
        public void run() {
    
                while (true) {
                    //注意synchronized包含的代码块的范围不能少也不能多
                    synchronized (this) {  //同步监视器(锁)可以使用this指代当前对象 WindowsIplementsSyn在此例中只创建了一个
                    if (ticket > 0) {
    
                        try {
                            Thread.sleep(50); //等待时间
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("当前窗口号" + Thread.currentThread().getName() + ":" + ticket);
                        ticket--;
                    } else {
                        break;
                    }
                }
            }
        }
    }
    
    public class WindowsIplementsSyn {
        public static void main(String[] args) {
            windowsSyn W1 = new windowsSyn(); //只创建了一个对象
    
            //多个线程共用一个对象
            Thread t1 = new Thread(W1);
            Thread t2 = new Thread(W1);
            Thread t3 = new Thread(W1);
            t1.start();
            t2.start();
            t3.start();
        }
    }
    继承Thread方式的代码中使用Synchronized同步代码块
    package main.java.ThreadExtends;
    
    /**
     * @Author lx
     * @Description 继承Thread方式的代码中使用Synchronized同步代码块
     * @Date 10:29 2020/8/5
     * @Version
     */
    class windows extends Thread{
    
        private static int ticket = 100;        //将ticket设为静态全局变量 只加载一次
       @Override
        public void run() {
            while (true) {
                //  synchronized (this) {  //在这里不能用this 因为此例中创建了多个对象 每进循环一次都是不同的对象,线程安全问题依旧存在
                synchronized (window.class) { //多个线程必须使用同一把锁 window.class 只会加载一次
    
                    if (ticket > 0) {
                        System.out.println("当前窗口号:" + Thread.currentThread().getName() + "还剩票数:" + ticket);
                        ticket--;
                    } else
                        break;
                }
            }
        }
    }
    public class WindowsExtendsSyn {
    
        public static void main(String[] args) {
    
            //创建了多个对象
            windows w1 = new windows();
            windows w2 = new windows();
            windows w3 = new windows();
            w1.start();
            w2.start();
            w3.start();
        }
    }
    
    
    
     

    方式二:同步方法
    * 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。

    
    

    * 关于同步方法的总结:
    * 1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
    * 2. 非静态的同步方法,同步监视器是:this
    *  静态的同步方法,同步监视器是:当前类本身

    1.实现Runnable接口方式的代码中使用Synchronized同步方法
    package main.java.ThreadImplements;
    
    /**
     * @Author lx
     * @Description  实现Runnable接口方式的代码中使用Synchronized同步方法
     * @Date 17:27 2020/8/5
     * @Version
     */
    class windows2 implements Runnable{
    
        private int ticket = 100;
        @Override
        public void run() {
    
            while (true) {
    
                show();
                }
        }
    
        private synchronized void show(){
            if (ticket > 0) {
    
                try {
                    Thread.sleep(50); //等待时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("当前窗口号" + Thread.currentThread().getName() + ":" + ticket);
                ticket--;
            }
        }
    }
    
    public class WindowImpMethod {
        public static void main(String[] args) {
            windows2 W1 = new windows2(); //只创建了一个对象
    
            //多个线程共用一个对象
            Thread t1 = new Thread(W1);
            Thread t2 = new Thread(W1);
            Thread t3 = new Thread(W1);
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    
    2.继承Thread方式的代码中使用Synchronized同步方法
    
    
    package main.java.ThreadExtends;
    
    /**
     * @Author  lx
     * @Description 继承Thread方式的代码中使用Synchronized同步方法
     * @Date 15:36 2020/8/5
     * @Version
     */
    
    class windows1 extends Thread{
    
        private static int ticket = 100;        //将ticket设为静态全局变量 只加载一次
        @Override
        public void run() {
            while (true) {
                show(); //调用当前类的静态show方法
            }
        }
    
        private synchronized static void show(){    //静态的同步方法 同步监视器是:当前类本身
            if (ticket > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("当前窗口号:" + Thread.currentThread().getName() + "还剩票数:" + ticket);
                ticket--;
            }
        }
    }
    public class WinExtendsMethod {
        public static void main(String[] args) {
    
            //创建了多个对象
            windows1 w1 = new windows1();
            windows1 w2 = new windows1();
            windows1 w3 = new windows1();
            w1.start();
            w2.start();
            w3.start();
        }
    }
    
    
    
     
  • 相关阅读:
    反转链表 16
    CodeForces 701A Cards
    hdu 1087 Super Jumping! Jumping! Jumping!(动态规划)
    hdu 1241 Oil Deposits(水一发,自我的DFS)
    CodeForces 703B(容斥定理)
    poj 1067 取石子游戏(威佐夫博奕(Wythoff Game))
    ACM 马拦过河卒(动态规划)
    hdu 1005 Number Sequence
    51nod 1170 1770 数数字(数学技巧)
    hdu 2160 母猪的故事(睡前随机水一发)(斐波那契数列)
  • 原文地址:https://www.cnblogs.com/lixia0604/p/13440021.html
Copyright © 2011-2022 走看看