CopyOnWrite简单使用
在集合遍历的时候,向集合中添加元素
这里的错误叫做fail-fast机制,当多个线程对同一集合的内容进行操作时,就可能会产生fail-fast事件
当modCount的数量和expectedModCount的数量不一致时,就会抛出上面的异常
集合元素添加产生异常的代码
package com.java.test.copyonwritearraylistdemo; /** * @Description: * @Author: Yourheart * @Create: 2022/12/2 15:34 */ public class TestCopyOnWriteArrayList { public static void main(String[] args) { HelloThread h = new HelloThread(); for (int i = 0; i < 10; i++) { new Thread(h).start(); } } }
package com.java.test.copyonwritearraylistdemo; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * @Description: * @Author: Yourheart * @Create: 2022/12/2 15:35 */ @Slf4j public class HelloThread implements Runnable { private static List<String> list = Collections.synchronizedList(new ArrayList<>()); static { list.add("aaa"); list.add("bbb"); list.add("ccc"); } @Override public void run() { Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ String next = iterator.next(); list.add(next+"123"); } } }
引入CopyOnWriteArrayList
不在使用modCount和ExpectModCount进行比较
代码改动部分
package com.java.test.copyonwritearraylistdemo; import lombok.extern.slf4j.Slf4j; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; /** * @Description: * @Author: Yourheart * @Create: 2022/12/2 15:35 */ @Slf4j public class HelloThread implements Runnable { private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); static { list.add("aaa"); list.add("bbb"); list.add("ccc"); } @Override public void run() { Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ String next = iterator.next(); list.add(next+"123"); } } }
不过问题也很明显,适合读数据比较多,写数据比较少的情况,会产生数据一致性问题
这里读取操作没有加锁,那么读数据就会很快,同时读取到的可能是老数据,而不是最新数据的原因
最后总结下优缺点
优点:读多写少性能优秀,线程安全读写分离
缺点:内存占用大,数据一致性问题
代码部分
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.java</groupId> <artifactId>test-study</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> </parent> <dependencies> <!--tomcat容器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--lombok依赖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> <!--引入junit单元测试依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!--判断空的用法 --> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc8 --> <dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>12.2.0.1</version> </dependency> <!--springboot整合mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <!--添加fastjson依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.70</version> </dependency> <!-- 热部署模块 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <!-- 这个需要为 true 热部署才有效 --> </dependency> <!--ThreadFactoryBuilder的依赖包,多线程使用--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1-jre</version> </dependency> <!--Lists.partition要用的依赖--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>21.0</version> </dependency> <!--ListUtils.partition使用的依赖--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> <finalName>study</finalName> </build> </project>
server.port=2001 logging.level.com.java.test=debug logging.level.web=debug spring.devtools.add-properties=false
package com.java.test; import com.java.test.config.ThreadPoolDemo; import com.java.test.copyonwritearraylistdemo.HelloThread; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CopyOnWriteArrayList; /** * @Description: * @Author: Yourheart * @Create: 2022/12/2 15:34 */ @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class TestCopyOnWriteArrayList { @Autowired private ThreadPoolDemo threadPoolDemo; @Test public void test() { CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); // while (true){ threadPoolDemo.createThreadPool().execute(new HelloThread(list)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } list.forEach(a->{ log.info(a); }); // } } }
package com.java.test.config; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import java.util.concurrent.*; /** * @Description: * @Author: Yourheart * @Create: 2022/11/25 14:43 */ @Component public class ThreadPoolDemo { @Bean public ExecutorService createThreadPool(){ /** * 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 * 说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销, * 解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。 */ ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() .setNameFormat("qiuxie-pool-%d").build(); ExecutorService singleThreadPool = new ThreadPoolExecutor(1, 2, 1000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); return singleThreadPool; } }
package com.java.test.copyonwritearraylistdemo; import lombok.extern.slf4j.Slf4j; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; /** * @Description: * @Author: Yourheart * @Create: 2022/12/2 15:35 */ @Slf4j public class HelloThread implements Runnable { private CopyOnWriteArrayList<String> list; public HelloThread(CopyOnWriteArrayList<String> list) { this.list = list; } @Override public void run() { Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ String next = iterator.next(); log.info("next:{}",next); list.add(next+"123"); } } }