模拟一个实例,用一个线程负责写数据,一个线程负责读数据,清单如下:
FileManager.java 负责读和写的具体实现 Main.java 主类,入口 TaskThread.java 自定义线程类 TaskRead.java 读任务 TaskWrite.java 写任务
FileManager.java
package com.iteye.badpie.javacode.thread.sync; import java.util.Random; public class FileManager { private Random mRandom; private String mName = "默认名字"; private String mEmail = "默认电子邮件"; public FileManager() { mRandom = new Random(); } public void write(String id, String name, String email) { mName = name; try { Thread.sleep(getRandomInt()); } catch (InterruptedException e) { e.printStackTrace(); } mEmail = email; System.out.println(String.format("写数据 id:%s, name:%s, email:%s", id, name, email)); } public void read(String id) { try { Thread.sleep(getRandomInt()); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(String.format("读数据 id:%s, name:%s, email:%s", id, mName, mEmail)); } private int getRandomInt() { return mRandom.nextInt(2000); } }
Main.java
package com.iteye.badpie.javacode.thread.sync; import com.iteye.badpie.javacode.thread.sync.TaskThread.ITask; public class Main { public static void main(String[] args) { System.out.println("主线程开始运行"); FileManager fileManager = new FileManager(); ITask taskWrite = new TaskWrite("w", fileManager); ITask taskRead = new TaskRead("r", fileManager); new TaskThread(taskWrite).start(); new TaskThread(taskRead).start(); System.out.println("主线程运行结束"); } }
TaskThread.java
package com.iteye.badpie.javacode.thread.sync; public class TaskThread extends Thread { private ITask mTask; public TaskThread(ITask task) { mTask = task; } @Override public void run() { mTask.execute(); } public interface ITask { public void execute(); } }
TaskRead.java
package com.iteye.badpie.javacode.thread.sync; import com.iteye.badpie.javacode.thread.sync.TaskThread.ITask; public class TaskRead implements ITask { private String mId; private FileManager mFileManager; public TaskRead(String id, FileManager fileManager) { mId = id; mFileManager = fileManager; } @Override public void execute() { for (int i = 0; i < 5; i++) { mFileManager.read(mId); } } }
TaskWrite.java
package com.iteye.badpie.javacode.thread.sync; import com.iteye.badpie.javacode.thread.sync.TaskThread.ITask; public class TaskWrite implements ITask { private String mId; private FileManager mFileManager; public TaskWrite(String id, FileManager fileManager) { mId = id; mFileManager = fileManager; } @Override public void execute() { for (int i = 0; i < 5; i++) { mFileManager.write(mId, i + "的名字", i + "的电子邮件"); } } }
某一次运行结果如下
主线程开始运行 主线程运行结束 写数据 id:w, name:0的名字, email:0的电子邮件 读数据 id:r, name:1的名字, email:0的电子邮件 写数据 id:w, name:1的名字, email:1的电子邮件 读数据 id:r, name:2的名字, email:1的电子邮件 读数据 id:r, name:2的名字, email:1的电子邮件 写数据 id:w, name:2的名字, email:2的电子邮件 读数据 id:r, name:3的名字, email:2的电子邮件 写数据 id:w, name:3的名字, email:3的电子邮件 读数据 id:r, name:4的名字, email:3的电子邮件 写数据 id:w, name:4的名字, email:4的电子邮件
分析一下结果,有明显的问题,写数据时,0的名字和0的电子邮件,1的名字和1的电子邮件等等,他们是一一对应的,但是在第一次读数据时就出现了数据错误,竟然把1的名字和0的电子邮件配对了。
可能导致此问题的原因是:在写数据时,当前数据只写了一半,就有读操作产生,于是就将本次的前半段数据(比如1的名字)和上一次的后半段数据(比如0的电子邮件)配对了。
解决此问题的方法是对写和读做同步,意思是说当我在写数据时就不允许读,在读数据时,不允许写,这样每次写或者读总是能够保证数据是统一的,配对是正确的。
java中的每一个实例对象都有一个锁,称之为lock,就像这样的场景:数据在屋子里,门上有一把锁,读数据的人和写数据的人都在门外排队,一个人进屋就将门反锁住,他读或者写完毕了打开门出来,再让下一个人进屋,这个人再将们反锁住,于是,这就能保持屋子里的数据始终是配对的,不会将第一个人的一半数据和第二个人的一半数据配对。
实现方式如下:
只需要对此处的FileManager做一个小小的改动就可以了(由synchronized关键字负责锁门)
package com.iteye.badpie.javacode.thread.sync; import java.util.Random; public class FileManager { private Random mRandom; private String mName = "默认名字"; private String mEmail = "默认电子邮件"; private Object mLock; public FileManager() { mRandom = new Random(); // 实例化一个锁 mLock = new Object(); } public void write(String id, String name, String email) { // 锁门 synchronized (mLock) { mName = name; try { Thread.sleep(getRandomInt()); } catch (InterruptedException e) { e.printStackTrace(); } mEmail = email; System.out.println(String.format("写数据 id:%s, name:%s, email:%s", id, name, email)); } // 打开门 } public void read(String id) { // 锁门 synchronized (mLock) { try { Thread.sleep(getRandomInt()); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(String.format("读数据 id:%s, name:%s, email:%s", id, mName, mEmail)); } // 打开门 } private int getRandomInt() { return mRandom.nextInt(2000); } }
再次执行,结果如下:
主线程开始运行 主线程运行结束 读数据 id:r, name:默认名字, email:默认电子邮件 读数据 id:r, name:默认名字, email:默认电子邮件 读数据 id:r, name:默认名字, email:默认电子邮件 读数据 id:r, name:默认名字, email:默认电子邮件 读数据 id:r, name:默认名字, email:默认电子邮件 写数据 id:w, name:0的名字, email:0的电子邮件 写数据 id:w, name:1的名字, email:1的电子邮件 写数据 id:w, name:2的名字, email:2的电子邮件 写数据 id:w, name:3的名字, email:3的电子邮件 写数据 id:w, name:4的名字, email:4的电子邮件
上面的锁也可以这样挂:
package com.iteye.badpie.javacode.thread.sync; import java.util.Random; public class FileManager { private Random mRandom; private String mName = "默认名字"; private String mEmail = "默认电子邮件"; public FileManager() { mRandom = new Random(); } public void write(String id, String name, String email) { // 锁门 对当前实例挂锁 synchronized (this) { mName = name; try { Thread.sleep(getRandomInt()); } catch (InterruptedException e) { e.printStackTrace(); } mEmail = email; System.out.println(String.format("写数据 id:%s, name:%s, email:%s", id, name, email)); } // 打开门 } public void read(String id) { // 锁门 synchronized (this) { try { Thread.sleep(getRandomInt()); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(String.format("读数据 id:%s, name:%s, email:%s", id, mName, mEmail)); } // 打开门 } private int getRandomInt() { return mRandom.nextInt(2000); } }
还可以这样挂锁:
package com.iteye.badpie.javacode.thread.sync; import java.util.Random; public class FileManager { private Random mRandom; private String mName = "默认名字"; private String mEmail = "默认电子邮件"; public FileManager() { mRandom = new Random(); } public synchronized void write(String id, String name, String email) { mName = name; try { Thread.sleep(getRandomInt()); } catch (InterruptedException e) { e.printStackTrace(); } mEmail = email; System.out.println(String.format("写数据 id:%s, name:%s, email:%s", id, name, email)); } public synchronized void read(String id) { try { Thread.sleep(getRandomInt()); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(String.format("读数据 id:%s, name:%s, email:%s", id, mName, mEmail)); } private int getRandomInt() { return mRandom.nextInt(2000); } }
相关推荐
在给定的信息中,我们可以看到一系列与Java编程相关的文件,标题和描述中虽然没有具体的代码内容,但通过标签"Java code"我们可以推断这是一组关于Java编程的源代码文件。下面,我们将根据常见的Java编程知识点,...
Java是一种广泛使用的面向对象的编程语言,以其跨平台性、高效性和丰富的类库而闻名。在Java编程中,"Java代码"通常指的是用Java语言编写的程序或脚本。在这个主题下,我们将深入探讨Java的基础知识,包括语法、类、...
4. **线程同步** - `synchronized`关键字:用于方法或代码块,确保同一时刻只有一个线程执行。 - `volatile`关键字:保证变量在所有线程间可见,防止指令重排序。 - `wait()`, `notify()`, `notifyAll()`:在同步...
`CountDownLatch`是Java并发包(java.util.concurrent)中的一个计数器类,它允许一个或多个线程等待其他线程完成操作。在初始化时,`CountDownLatch`需要一个非负整数作为计数器的初始值。每次调用`countDown()`...
多线程是Java的一大特色,Java代码实例会展示如何创建线程、同步机制(如synchronized关键字、Lock接口)、线程池的使用,以及并发工具类(如CountDownLatch、CyclicBarrier、Semaphore等)的应用。 网络编程是Java...
在本资源"JAVA code example 100 例"中,我们拥有一系列精心挑选的Java编程示例,旨在帮助开发者深入理解和应用Java语言。这个压缩包包含了从基础到高级的100个源代码实例,覆盖了Java编程的多个重要方面。下面,...
2. **并发编程**:详细讨论了线程、同步机制、并发工具类,如Semaphore、CyclicBarrier等,以及如何处理线程安全问题。 3. **网络编程**:涵盖了套接字编程,客户端-服务器模型,以及相关的网络API。 4. **GUI编程**...
1. **基础语法**:Java是一种面向对象的编程语言,其基础语法包括变量声明、数据类型(如int, String等)、运算符(如赋值、比较、逻辑等)、控制结构(if语句、for循环、while循环)、方法定义与调用。 2. **类与...
在多线程下载中,线程同步是关键,以确保数据的正确合并。这通常通过共享对象(如`DownInfo`类中的`startPos`和`endPos`)和适当的同步机制(如`synchronized`关键字或`Lock`接口)来实现,防止不同线程同时修改...
Java编程语言是世界上最流行的开发平台之一,以其跨平台、面向对象和安全性著称。这个名为"java-code Java语言程序.zip"的压缩包很可能是包含了一系列的Java源代码文件,供学习、参考或直接使用。在Java编程中,我们...
在Java中,我们可以使用`synchronized`关键字或者`java.util.concurrent`包下的工具类来实现线程同步,防止数据竞争。例如,可以使用`synchronized`方法或代码块来控制对共享资源的访问,确保同一时间只有一个线程...
《Java实例手册Codesamples》是SUN公司发布的一份详细且实用的编程资源,主要针对Java编程语言。这本书籍包含了丰富的代码示例,旨在帮助开发者深入理解和应用Java技术。以下是一些关键知识点的概述: 1. **Java...
在计算机编程领域,尤其是网络编程中,线程同步与异步套接字编程是至关重要的概念,它们直接影响到程序的性能、稳定性和可扩展性。本文将深入探讨这两个主题,并结合实际应用进行详细阐述。 首先,我们需要理解线程...
在实际开发中,合理使用线程和线程同步技术可以提升程序性能,同时保证程序的正确性和稳定性。本示例提供的代码可能包含了这些概念的应用,具体实现需查看解压后的`codefans.net`文件以获取更多信息。
理解和掌握线程同步机制,如`synchronized`关键字、`wait()`, `notify()`和`notifyAll()`方法,以及`Lock`接口,是编写高效并发程序的关键。 6. **异常处理**: Java的异常处理机制通过`try-catch-finally`块来捕获...
3.Using conditions in synchronized code; 4.Synchronizing a block of code with a Lock; 5.Synchronizing data access with read/write locks 6.Modifying Lock fairness 7.Using multiple conditions in a Lock
本资源"jibu_java_1.0.0.zip"提供了一个名为"jibu_java_1.0.0"的Java并行编程库,它专为Java 1.0.0设计,旨在帮助开发者高效地编写多线程、多核并行的程序。 并行编程是将一个大型任务分解成若干个可以同时执行的小...