首页 > 其他分享 >多线程中的安全问题

多线程中的安全问题

时间:2022-08-19 13:58:18浏览次数:51  
标签:同步 synchronized Thread 静态 问题 安全 ticket 多线程 public

目录

synchronized

synchronized格式

  1. 同步代码块的格式:
    synchronized (锁对象){...}
  2. 非静态同步方法的格式:
    public synchronized 方法返回值 方法名称 (){....}
  3. 静态同步方法的格式:
    public static synchronized 方法返回值 方法名称(){....}

synchronized的同步代码块

1.使用同步代码块解决多线程安全问题

***使用同步代码块实现多线程卖票***
使用继承Thread实现多窗口卖票
Thread实现类:
//使用继承Thread类实现卖票
public class MyThread extends Thread{
    public static int ticket = 20;
    public static Object obj = new Object();//设置锁对象

    public MyThread(String name) {
        super(name);
    }
    public void run(){
        while (true){
            synchronized (obj){
//                synchronized (MyThread.class){
            if (ticket <= 0){
                System.out.println("票卖完了");
                break;
            }else{
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"张票");
                ticket--;
            }
          }
        }
    }
}
测试类:
public class Test01 {
    public static void main(String[] args) {
        MyThread myThread1 = new MyThread("李哈哈");
        MyThread myThread2 = new MyThread("李嘿嘿");
        myThread1.start();
        myThread2.start();
    }
}

2.使用同步代码块解决多个入口执行共享资源问题

多个入口操作共享资源,实现同步代码块多窗口卖票
Thread实现类:
//实现多个同步代码块进行多入口操作共享资源
public class MyThread extends Thread{
    public static int ticket = 60;

    public MyThread(String name) {
        super(name);
    }
    public void run(){
        while (true){
            if (ticket == 0){
                break;
            }
            if (ticket % 2 == 0){
                synchronized (this){//必须保证和所有的同步代码块的锁对象相同
                    if (ticket > 0){
                        System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"票");
                        ticket--;
                    }
                }
            }else{
                synchronized (this){//必须保证和所有的同步代码块的锁对象相同
                    if (ticket > 0){
                        System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"票");
                        ticket--;
                    }
                }
            }
        }
    }
}
测试类:
public class Test01 {
    public static void main(String[] args) {
        MyThread myThread1 = new MyThread("李哈哈");
        MyThread myThread2 = new MyThread("李嘿嘿");
        myThread1.start();
        myThread2.start();
    }
}

synchronized的非静态同步方法

1.使用非静态同步方法解决多线程安全问题

***使用静态同步方法***
使用接口Runnable和非静态同步方法多窗口卖票
Runnable接口的实现类:
public class MyRunnable implements Runnable{
    public static int num = 20;
    @Override
    public void run() {
        while (true){
            if (num == 0){
                break;
            }else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                method();
            }
        }

    }
    public synchronized void method(){//锁的对象默认是this
        if (num > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
            num--;
        }
    }
}

测试类:
public class Test01 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable, "窗口1");
        Thread t2 = new Thread(myRunnable, "窗口2");
        t1.start();
        t2.start();
    }
}

2.使用非静态同步方法解决多个入口执行共享资源问题

多个入口操作共享资源,实现非静态同步方法多窗口卖票
//使用非静态实现多入口多窗口卖票
public class MyRunnable implements Runnable{
    public static int num = 20;
    @Override
    public void run() {
        while (true){
            if (num == 0){
                break;
            }else if (num % 2 == 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                method1();
            }else {
                method();
            }
        }

    }
    public synchronized void method(){//锁的对象默认是this
        if (num > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
            num--;
        }
    }
    public synchronized void method1(){//锁的对象默认是this
        if (num > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
            num--;
        }
    }
}
测试类:
public class Test01 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable, "窗口1");
        Thread t2 = new Thread(myRunnable, "窗口2");
        t1.start();
        t2.start();
    }
}

synchronized的静态同步方法

1.使用静态同步方法解决多线程安全问题
使用静态同步方法

使用Runnable接口和静态同步方法实现多窗口卖票
//使用静态同步方法实现多窗口卖票
public class MyRunnable implements Runnable {
    public static int ticket = 40;
    @Override
    public void run() {
        while (true){
            if (ticket == 0){
                break;
            }else{
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                method();
            }
        }
    }
    public static synchronized void method(){
        if (ticket > 0){
            System.out.println(Thread.currentThread().getName()+"卖了"+ticket+"张票");
            ticket--;
        }
    }
}

测试类:
public class Test01 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable, "小姐姐");
        Thread t2 = new Thread(myRunnable, "大姐姐");
        t1.start();
        t2.start();
    }
}

2.使用非静态同步方法解决多个入口执行共享资源问题

使用多个静态同步方法实现多卤肉执行共享资源
//使用静态同步方法实现多入口多窗口卖票
public class MyRunnable implements Runnable{
    public static int num = 20;
    @Override
    public void run() {
        while (true){
            if (num == 0){
                break;
            }else if (num % 2 == 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                method1();
            }else {
                method();
            }
        }

    }
    public static synchronized void method(){//锁的对象默认是当前类的CLass对象
        if (num > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
            num--;
        }
    }
    public static synchronized void method1(){//锁的对象默认是当前类的CLass对象
        if (num > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
            num--;
        }
    }
}
测试类:
public class Test01 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable, "a");
        Thread t2 = new Thread(myRunnable, "b");
        t1.start();
        t2.start();
    }
}

多入口和多窗口卖票的不同情况

多入口操作共享资源时,使用同步代码块和静态同步方法,非静态同步方法的搭配使用,实现多窗口卖票

多入口的操作共享资源的多种情况:

  1. 多个入口都是同步代码块时:需要保持多个同步代码块对的锁对象是同一个。
  2. 多个入口有同步代码块和非静态同步方法时:需要将同步代码块中的锁对象修改为this,保证同步代码块和非静态同步方法的锁对象一致
  3. 多个入口有同步代码块和静态同步方法时:需要将同步代码块中的锁对象修改为当前类的Class对象,有两种方式修改同步代码块中的锁改为Class对象(前类名.class或者this.getClass),这样保证了同步代码块和静态同步方法的锁对象一致。

代码实现

1.同步代码块+非静态同步方法+多入口操作共同资源
思路:
需要使得同步代码块中的锁对象和非静态同步方法中的锁对象相同,但是非静态方法中的锁默认为this,当前类,所以只有修改同步代码块中的锁对象也为this,才能保证两者锁对象相同,多个入口的锁应该使用相同的锁,所以使用同一种非静态同步方法即可。
代码块:

同步代码块+非静态同步方法
//使用同步方法解决多线程安全问题
//如果有多个入口可以操作共享的资源
//可以同时使用同步代码块和同步方法
public class MyRunnable implements Runnable{
    public static int ticket = 40;
    @Override
    public void run() {
        while (true){
            if (ticket % 3 == 0){
//            使用同步代码块
                synchronized (this){
                    if (ticket > 0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
                        ticket--;
                    }
                }
            }else{
//                使用同步方法
                method();
            }
        }
    }
    public synchronized void method(){//同步方法的锁对象默认是this
        if (ticket > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
            ticket--;
        }
    }
}

2.同步代码块+静态同步方法+多入口操作共同资源

思路:
需要同步代码块中的锁对象和静态同步方法的锁对象保持一致,静态同步方法中的锁对象默认为当前类的Class对象,所以修改同步代码块中的锁对象使得两者保持一致即可。
代码块:

同步代码块+静态同步方法+多入口操作共享资源
//使用同步静态方法解决多线程安全问题
//如果有多个入口可以操作共享的资源
//可以同时使用同步代码块和静态同步方法
public class MyRunnable implements Runnable{
    public static int ticket = 40;
    @Override
    public void run() {
        while (true){
            if (ticket % 3 == 0){
//            使用同步代码块
//                synchronized (MyRunnable.class){
                    synchronized (this.getClass()){
                    if (ticket > 0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
                        ticket--;
                    }
                }
            }else{
//                使用静态同步方法
                method();
            }
        }
    }
    public static synchronized void method(){//静态同步方法的锁对象默认是当前类的class对象
        if (ticket > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
            ticket--;
        }
    }
}

Lock锁

使用Lock实现多线程卖票:

Lock解决多线程安全问题
//使用锁实现多线程卖票
public class MyRunnable implements Runnable{
    private static int ticket = 100;
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            if (ticket == 0){
                System.out.println("票卖完了");
                break;
            }else {
                lock.lock();
                if (ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"票");
                    ticket--;
                }
            }
            lock.unlock();
        }
    }
}

测试类:
public class Test01 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable,"窗口1");
        Thread t2 = new Thread(myRunnable,"窗口2");
        Thread t3 = new Thread(myRunnable,"窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

标签:同步,synchronized,Thread,静态,问题,安全,ticket,多线程,public
From: https://www.cnblogs.com/lihaha520-1314/p/16594561.html

相关文章

  • Java实现多线程的四种方式
    java中实现多线程主要有四种方式:继承Thread类一,继承Thread类,重写run方法publicclassThreadTest{//主线程publicstaticvoidmain(String[]args){......
  • java:关于打包时的资源获取问题
    我们有时会遇上打包后,图片资源不显示等问题,我总结了以下两个方法:1.将资源文件放在外部,不打入包中,此时直接用相对路径或绝对路径就可以获取资源文件;比如将资源文件与打好的......
  • 解决python import找不到自定义包的问题
    文件结构如下├──A│├──a.py│├──__init__.py│└──b.py├──B│├──c.py│├──__init__.py│└──d.py想在c.py调用......
  • 完全背包问题
    #include<iostream>//01背包问题状态转移方程dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+p[i])区别:因为物品只能装一次所以在比较装入物品后的价值时使用i-1而不是i因为......
  • Win10桌面图标显示问题
    原文链接Windows桌面图标出现模糊、阴影等显示问题。解决办法1.在桌面上鼠标右键→个性化→主题→桌面图标设置→把计算机回收站用户的文件控制面板网络等全部不选......
  • 使用阿里读取 excel文件遇到的问题
    Servlet.service()forservlet[dispatcherServlet]incontextwithpath[]threwexception[Requestprocessingfailed;nestedexceptioniscom.alibaba.excel.exc......
  • 如何解决电机控制器引起的电机效率和温升问题
    无论是什么种类的电机控制器或变频器,在运行的过程中均存在不同程度的谐波电压和电流,使电动机在非正弦电压、电流下运行。谐波会引起电机定子铜耗、转子铜耗、铁耗及附加损......
  • npm link 执行脚本,最后却打开了脚本文件本身的问题
    在编写node自定义的一些脚本命令的时候,使用npm link打开bin的执行文件,本意是要执行执行文件里面的内容,却发现最后文件本身被打开了,后来发现是要做一个声明再执行文件头......
  • 解决Docker容器 iptables问题---docker: Error response from daemon: driver failed
    一、问题如下[root@echohyeapp]#dockerrun-itd--namemysql-test-p3306:3306-eMYSQL_ROOT_PASSWORD=123456mysql:5.75ae9dc1c7bf16762e7064a5f3ab0396b1f5ba3a......
  • 关于SpringBoot整合redis使用Lettuce客户端超时问题
    问题起因使用到Lettuce连接redis,一段时间后不操作,再去操作redis,会报连接超时错误,在其重连后又可使用。原因是:Lettuce自适应拓扑刷新(Adaptiveupdates)与定时拓扑刷新(Peri......