Logo

郎哥编程

实例说明多线程数据同步存在的问题

2018-05-06 899
应用多线程技术可以提高应用程序的响应能力,充分利用计算机的CPU资源,为用户提供更好的应用体验。但也会存在因多个线程竞争使用并修改同一数据而产生数据同步的问题。

举一个例子来说明这个问题。考虑一个编程任务场景,一个协同写作系统允许多个作者共同编写图书,任务要求是每个作者可以添加内容到图书,但不能修改和删除别人的内容,作者对内容编辑完成后,需要将自己编辑的内容添加到图书中。

要完成该编程任务,需要定义一个Book类,用于存储图书内容;定义一个Author线程类,用于编辑内容并将编辑后的内容添加到Book对象。

(1)定义一个Book类,设置属性content,存储图书内容,代码如下:

package com.milihua.bookwriter;
public class Book {                  
    String content;
    public Book()
    {
     content = "";
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
 }

(2)定义一个Author线程类,设置属性name、docBook,name存储作者名称,docBook对象为传入的图书对象。在run方法中,让线程休眠1000毫秒,模拟创作过程。代码如下:

package com.milihua.bookwriter;
public class Author implements Runnable{
    Book docBook;
    //作者姓名
    String  name;
    public Author(Book inBook,String inName)
    {
     docBook = inBook;
     name = inName;
    }
   
    public void setDocBook(Book inBook)
    {
     docBook = inBook;
    }
   
    @Override
    public void run() {
        // TODO Auto-generated method stub
        //模拟创作,让线程等待1000毫秒
        try {
            Thread.sleep(1000);
            //编辑内容并添加到docBook对象
            String str = docBook.getContent() + "\n" + name + ":我的创作内容";
            docBook.setContent(str);
           
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

(3)建立主线程类,创建5个作者线程,开始模拟图书内容创作。代码如下:

package com.milihua.bookwriter;
public class {
    public static void main(String[] args) {
        Book  docBook = new Book();
        //创建5个作者
        Thread t1 = new Thread(new Author(docBook,"李某"));
        t1.start();
        Thread t2 = new Thread(new Author(docBook,"王新"));
        t2.start();
        Thread t3 = new Thread(new Author(docBook,"张某"));
        t3.start();
        Thread t4 = new Thread(new Author(docBook,"赵三"));
        t4.start();
        Thread t5 = new Thread(new Author(docBook,"李四"));
        t5.start();
        try {
            Thread.sleep(10000);
            System.out.println("共同创作的图书内容:\n" + docBook.getContent());
 
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

运行程序,在控制台得到输出结果如下图所示:

     

00035.png

                                       

图 15-11 BookWriter示例程序输出结果

观察输出结果,发现程序输出有些问题,少了一个作者的创作内容,多次运行程序,发现缺少哪个作者并没有规律。这显然不符合程序要求,检查程序也没有发现其它问题。这个隐藏的问题就是多线程数据同步的问题,多个作者线程试图同时更新图书对象内容,导致部分作者创作内容丢失。

问题应该出在Author线程类编辑内容和添加内容的代码上,编辑内容和添加内容的代码如下:

try {
            Thread.sleep(1000);
            //编辑内容并添加到docBook对象
            String str = docBook.getContent() + "\n" + name + ":我的创作内容";
            docBook.setContent(str);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

假设有A、B两个Author线程,A 线程先于B线程执行了docBook.getContent()语句,该语句执行完成后,A线程获取了当前图书内容,当A线程要执行docBook.setContent(str)语句时,A线程的运行被中断了(JVM分配给它的时间片用完了)。这时,假设B线程被执行,并且顺利完成了docBook.getContent()和docBook.setContent(str)语句,B线程编辑的内容已经存储到Book对象中。当A线程再次被执行时,A线程的docBook.setContent(str)语句将重写Book对象的内容,因为A线程已经在B线程之前获取了Book对象的内容,将会导致B线程写入到Book对象的内容被覆盖掉。

要解决Book对象同步的问题,就需要用到Java提供的线程同步方法,下面一节我们将介绍利用synchronized方法解决本节案例遇到的问题。

■ 知识点拨

在多线程应用程序中,如果多个线程对共享数据仅是使用,则无需对共享数据做同步保护。当线程要对共享数据进行修改时,必须要对修改的共享数据进行同步保护,否则会出现莫名其妙的问题,程序出现的BUG也不好定位。


代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论