首页 > 其他分享 >多线程分时系统线程安全问题-synchronize

多线程分时系统线程安全问题-synchronize

时间:2022-09-27 20:59:54浏览次数:51  
标签:Thread ++ t2 t1 synchronize int 多线程 public 分时系统

问题

多线程分时系统是存在线程安全问题的,如下例子:

两个线程分别对同一个变量(初始值 = 0)做循环自增和自减操作各50000次,观察结果,并不等于初始值。

public class 分时系统线程安全问题 {
    static int a = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                a++;
            }
        },"t1");

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                a--;
            }
        },"t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(a);
    }
}

出现以上问题的原因就是,看似单个的Java语句(a++、a--)会被操作系统拆分为多几条指令。自增自减并不是原子操作。在多线程的情况下下面的8条指令是会交错执行的,必然导致结果!=预期

a++:

getstatic       i  //获取静态常量的值

iconst_1         //准备常量1 

iadd             //自增

putstatic       //讲修改后的值写入静态变量i

a--:

getstatic       i  //获取静态常量的值

iconst_1         //准备常量1

isub             //自减

putstatic       //讲修改后的值写入静态变量i

临界区 Critical Section

一个程序运行多个线程本身没有问题,问题出在多个线程访问了共享资源,多个读写共享资源其实也没问题,但是多个线程在访问共享资源是出现了指令交错就会有问题。

一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区

static int counter = 0;
static void increment(){
    //临界区
    counter++;
}

static void decrement(){
    //临界区
    counter--;
}

竞态条件 Race condition

多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件

解决问题

1、应用之互斥

为了避免临界区的竞态条件发生,有多种手段可以达到目的。

阻塞式的解决方案:synchronized,Lock

非阻塞式的解决方案:原子变量 

synchronized

语法

synchronize(对象){
    //临界区
}

static int a = 0;
/**
 * 对象锁
 */
static Object lock = new Object();

/**
 * 线程安全
 */
public static void test2() throws InterruptedException {
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 50000; i++) {
            synchronized (lock) {
                a++;
            }
        }
    }, "t1");

    Thread t2 = new Thread(() -> {
        for (int i = 0; i < 50000; i++) {
            synchronized (lock) {
                a--;
            }
        }
    }, "t2");
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(a);
}

synchronize利用对象锁保证了临界区内的代码块的原子性(不可分割)。

过程分析:

思考:

1、synchronize放在for循环外面能保证结果正确吗?(--原子性)

 

synchronized (lock) {
    for (int i = 0; i < 50000; i++) {
        a++;//a--;
    }
}

答案:可以的synchronize关键字包含的代码块为一个整体不可分割。

2、t1里面synchronize(lock1)、t2里面synchronize(lock2)能保证结果正确吗?(--锁对象)

答案:不能,加锁要是同一个对象锁、保护共享资源,必须保证同一个对象锁。

3、t1加锁 synchronize(lock),t2不加锁能保证结果正确吗?(--锁对象)

答案:不能,同问题2.

 

面向对象的优化:

public class 分时系统线程安全问题2 {


    public static void main(String[] args) throws InterruptedException {
        Room room = new Room();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                room.increment();
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                room.decrement();
            }
        }, "t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(room.getA());

    }
}

class Room {
    private int a = 0;

    public void increment() {
        synchronized (this) {
            a++;
        }
    }

    public void decrement() {
        synchronized (this) {
            a--;
        }
    }

    public int getA() {
        return a;
    }
}

synchronize的使用发:

1、加在普通法放上。

class Test{
    public synchronized void test() {

    }
}
//等价于
class Test{
    public void test() {
        synchronized(this) {

        }
    }
}

2、加在静态方法上

class Test{
    public synchronized static void test() {
    }
}
//等价于
class Test{
    public static void test() {
        synchronized(Test.class) {

        }
    }
}

标签:Thread,++,t2,t1,synchronize,int,多线程,public,分时系统
From: https://www.cnblogs.com/ieas/p/16735928.html

相关文章

  • 多线程详解
    线程简介任务、进程、线程、多线程Process进程Thread线程线程就是独立的执行路径在程序运行时,即使没有自己创建线程,后台也会有多个线程,如:主线程,gc线程;main()称之为......
  • 多线程环境下安全的集合
    多线程环境下安全的集合List/***并发修改异常多个线程同时操作一个不安全的集合*<p>*CopyOnWriteArrayList写时复制技术*add方法会先复制一个新数组对新......
  • Day 12 synchronized和Lock的学习
    Day12多线程学习同步方法及同步块方法锁synchronized可以保证线程的同步。形成原理就是队列和锁在方法前加synchronized关键字这个方法就是同步方法,没有加就不安全。......
  • C++多线程编程之【线程管理】
    1.如何启动线程?构建std::thread对象即可。直接传函数名(地址)创建一个类并创建伪函数。构建对象(实例化),将对象作为参数传入thread对象实例化。2.为什么要等待线程?首先......
  • 多线程
    1.线程和进程区别线程的划分尺度小于进程,这使多线程程序拥有高并发性,进程在运行时各内存单元之间相互独立,线程之间内存共享,这使多线程拥有更好的性能和用户体验。线程......
  • 【转载】Python -- 多进程、多线程 的基本使用
    https://www.cnblogs.com/jiyu-hlzy/p/15948408.html 单进程单线程importtimedefproduction():"""间隔一秒,模拟一秒生产一个任务,生产10个任务:ret......
  • Java多线程
    join()方法使用:【已经开了3个线程ABC,要求线程A在线程B的前面执行,线程B在线程C的前面执行】https://blog.csdn.net/zds448588952/article/details/99613648......
  • 多线程——Robyn编程学习(Java)
    多线程的作用能够创建多个线程,此外线程可以体现程序的动态性,提高效率,在抢票以及各种游戏之中具有非常重要的作用。(线程的魅力在坦克大战中体现的淋漓尽致)多线程的知识体......
  • 多线程批量删除后再查询所遇到的坑
    问题:以前对于多线程异步执行没有细想,认为已经采用的countdownlatch,保证了任务全部执行完毕。实际上,并不是我认为的那样。首先,一个方法是多线程异步批量删除数据,另一个方......
  • Day11 多线程的学习,线程一些方法的使用
    Day11多线程的学习线程休眠sleep的使用sleep可以模拟网络延迟和倒计时。模拟网络延迟在线程中使用Thread.sleep()方法,能够放大问题的发生性,能更好的把握问题所在。比......