十三、java多线程基础之小案例二

一、背景

1.前面讲解了很多的基础理论知识,现在我们开始几个简单点的小栗子,来巩固下我们前面学习的基础知识,接下来我们来具体来讲解下例子吧。

2.接着我们第一篇的小案例继续讲解,然后我们开始来讲解我们今天的小案例,好了,废话不多说了,直接开始来讲解吧。

二、项目图

1.项目结构

三、代码组成

1.pom.xml

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.0.0</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.47</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

2.application.properties

#配置项目端口
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/threadtest?serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver

3.ThreadPoolTaskExecutorConfig.java

@Configuration
public class ThreadPoolTaskExecutorConfig {
    private static int CORE_POOL_SIZE = 5;
    private static int MAX_POOL_SIZE = 1000;
    @Bean(name="threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor serviceJobTaskExecutor(){
        ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
        //线程池维护线程的最少数量
        poolTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        //线程池维护线程的最大数量
        poolTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        //线程池所使用的缓冲队列
        poolTaskExecutor.setQueueCapacity(200);
        //线程池维护线程所允许的空闲时间
        poolTaskExecutor.setKeepAliveSeconds(30000);
        poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        System.out.println(poolTaskExecutor);
        return poolTaskExecutor;
    }
}

4.DataSourceConfig.java

@Component
@Configuration
@MapperScan(basePackages = "com.thread.threadtest.dao", sqlSessionTemplateRef  = "systemSqlSessionTemplate")
public class DataSourceConfig {
	@Bean(name = "systemDataSource")
	@Primary
    @ConfigurationProperties(prefix = "spring.datasource.primary") // application.properteis中对应属性的前缀
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }
	@Bean(name = "systemSqlSessionFactory")
    @Primary
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("systemDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        bean.setTypeAliasesPackage("com.thread.threadtest.pojo");
        return bean.getObject();
    }
    @Bean(name = "systemTransactionManager")
    @Primary
    public DataSourceTransactionManager testTransactionManager(@Qualifier("systemDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    @Bean(name = "systemSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("systemSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

5.UserVoController.java

@RestController
@RequestMapping("/user")
public class UserVoController {

    @Autowired
    private UserVoService userVoService;

    @RequestMapping("/submit")
    public String submit(String param){
        param = userVoService.submit(param);
        return param;
    }

    @RequestMapping("/execute")
    public String execute(String param){
        param = userVoService.execute(param);
        return param;
    }
}

6.UserVoService.java

public interface UserVoService {
    String submit(String param);
    String execute(String param);
}

7.UserVoServiceImpl.java

@Service
public class UserVoServiceImpl implements UserVoService {

    @Autowired
    private ThreadPoolTaskExecutorConfig threadPoolTaskExecutorConfig;
    @Autowired
    private UserVoDao userVoDao;

    @Override
    public String submit(String param) {
        Future<String> future = threadPoolTaskExecutorConfig.serviceJobTaskExecutor().submit(new UserVoCallableTask(param));
        try {
            param = future.get();
        } catch (Exception e) {
            e.printStackTrace();
            return "error";
        }
        UserVo user = new UserVo();
        user.setName(param);
        userVoDao.insertUserVo(user);
        return param;
    }

    @Override
    public String execute(String param) {
        threadPoolTaskExecutorConfig.serviceJobTaskExecutor().execute(new UserVoRunnableTask(param, userVoDao));
        return "success";
    }
}

8.UserVoDao.java

public interface UserVoDao {
    void insertUserVo(UserVo user);
}

9.UserVo.java

public class UserVo {
    private int id;
    private String ucode;
    private String name;
    private String sex;
    private String pwd;
    private int sid;
    private String school;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUcode() {
        return ucode;
    }

    public void setUcode(String ucode) {
        this.ucode = ucode;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }
}

10.UserVoCallableTask.java

public class UserVoCallableTask implements Callable<String> {
    private String param;
    public UserVoCallableTask(String param){
        this.param = param;
    }
    @Override
    public String call() throws Exception {
        param +="UserVoCallableTask";
        int a = 1/0;
        return param;
    }
}

11.UserVoRunnableTask.java

public class UserVoRunnableTask implements Runnable {

    private String param;
    private UserVoDao userVoDao;
    public UserVoRunnableTask(String param, UserVoDao userVoDao) {
        this.param = param;
        this.userVoDao = userVoDao;
    }
    @Override
    public void run() {
        param += "UserVoRunnableTask";
        UserVo user = new UserVo();
        user.setName(param);
        int a = 1/0;
        userVoDao.insertUserVo(user);
    }
}

12.ThreadtestApplication

@SpringBootApplication
@ComponentScan(basePackages = {"com.thread.threadtest.*"})
public class ThreadtestApplication {
    public static void main(String[] args) {
        SpringApplication.run(ThreadtestApplication.class, args);
    }
}

13.请求submit接口:

14.请求execute接口:

15.总结

1.Callable执行call时遇到异常会抛出,而Runnable执行run时遇到异常并不会抛出。

四、多线程与事务回滚:

上述,如果在事务中调用了多线程,submit遇到异常会抛出且必须被捕获,不会触发回滚,execute遇到异常主线程无法感知,也不会触发回滚。那如果需要在多线程调用时实现事务回滚该怎么做呢?这就需要加入其它的操作了:

1、submit方法与事务回滚:我们知道sumbit方法提交线程在获取返回结果时是需要捕获异常的,那么我们就可以在捕获到异常时手动回滚当前事务。

(1)主线程正常,子线程发生异常,只回滚主线程:这种情况比较简单,主线程捕获异常后直接TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();回滚主线程就可以了:
1.1.UserServiceImpl.java



@Service("userService")
public class UserServiceImpl implements UserService {
 
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
 
    @Autowired
    private UserMapper userMapper;
 
    @Override
    @Transactional
    public String submit(String param) {
 
 
        Future<String> future = threadPoolTaskExecutor.submit(new UserCallableTask(param,userMapper));
        UserDTO user = new UserDTO();
        user.setName("我是主线程");
        userMapper.insert(user);
        try {
            param =  future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return "error";
        } catch (ExecutionException e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return "error";
        }
 
        return param;
    }
 
    @Override
    public String execute(String param) {
        threadPoolTaskExecutor.execute(new UserRunnableTask(param,userMapper));
        return "success";
    }
}

1.2.UserCallableTask.java


 public class UserCallableTask implements Callable<String> {
 
    private String param;
 
    private UserMapper userMapper;
 
    public UserCallableTask(String param, UserMapper userMapper){
        this.param = param;
        this.userMapper = userMapper;
    }
    @Override
    public String call() throws Exception {
        param += "UserCallableTask";
        UserDTO user = new UserDTO();
        user.setName("我是子线程");
        userMapper.insert(user);
        int a = 1/0;
        return param;
    }
}

3.结果

4.数据库没有主线程的数据插入:

说明主线程回滚成功。 

(2)、主线程或子线程异常,主线程、子线程全部回滚:同时回滚主线程和子线程,就需要把主线程和子线程放到同一个事务中。说明主线程、子线程全部回滚成功。

参考博客:https://blog.csdn.net/w_t_y_y/article/details/102817576

五、结束

Always keep the faith!!!

发布了122 篇原创文章 · 获赞 64 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/chenmingxu438521/article/details/103895350