• 解决线程安全-------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();
        }
    }
    
    
    
     
  • 相关阅读:
    【C++ 系列笔记】03 C++ 面向对象进阶
    【C++ 系列笔记】02 C++ 面向对象基础
    【C++ 系列笔记】01 C++ 与 C
    【JavaScript】简单取随机数 ~~(Math.random() * number)
    英语测试
    Linux指令入门
    RE-攻防世界 T3 insanity
    PWN-攻防世界 level0
    ISCC不会的理论题
    kali linux配置ssh
  • 原文地址:https://www.cnblogs.com/lixia0604/p/13440021.html
Copyright © 2020-2023  润新知