线程-03

简介

继上一次的线程02,有问题欢迎指正

1.线程的等待和通知

如果存在同步方法或同步块,JVM调用monitor对线程进行监控,线程进入方法或代码块后,持有锁对象,其他线程不能进入,线程执行完方法或代码块,释放锁,其他线程都可以进入。
任何java对象都可以作为锁使用,因为在Object类中定义线程操作相关方法。
wait()----让当前持有锁的线程一直等待
wait(long time)—让当前持有锁的线程等待一段时间
notify()—随机选取一个等待的线程,进行通知继续执行
notifyAll()—通知所有等待的线程继续执行

注意:上面几个方法必须是锁对象调用,否则将出现illegalMonitorStateException锁对象是同步代码块括号中的对象,同步方法中的this。

2.生产者消费者模式

和线程相关的设计模式,不是传统的Gof23种设计模式。

生产者消费者模式,是用于解决生产者和消费者速度不一致的问题

在线程的世界中生产和消费的是数据,用于产生数据的可以看做是生产者,需要获得数据进行运算的可以看做消费者吗,线程的执行时独立的,速度有快有慢,如果生产者速度过快,会造成资源浪费,如果消费速度过快,会让消费线程无效执行,需要协调生产者消费者的速度。

解决方案:
1.需要建立缓冲区(集合或者数组用于存储数据),需要有上限
2.生产者生产的数据存入到缓冲区中,如果缓冲区满了,就让生产者线程等待,如果没有满,就通知所有等待的生产者生产和消费者消费
3.消费者从缓冲区获得数据并删除,如果缓冲区空了,就让消费者线程等待,如果没有空,就通知所有等待的生产者和消费者。

综上:线程+缓冲区+等待+通知

3.阻塞队列

是一种集合,提供存取方法,能在数据数量达到临界值时自动阻塞当前线程,能在数据数量为空时阻塞当前线程。

BlockingQueue<E>接口
void put(E e)—添加数据,满了会阻塞
E take()–取列头数据并删除,空了会阻塞

常用实现类:
ArrayBlockingQueue—数组结构阻塞队列 (查询效率高)
LinkedBlockingQueue—链表结构阻塞队列(插入、删除效率高)

4.线程池

作用:对线程进行回收利用,线程对操作系统而言是宝贵的资源,频繁创建和销毁会浪费系统资源,降低程序性能。

Executor 接口
方法 : execute(Runnable command)
启动线程来执行任务,线程执行完成后会回到线程池中,等待下一个任务
ExecutorService接口 继承 Executor接口
方法:shutdown,shutdownNow,submit
实现类:
ThreadPoolExcecutor 线程池的实现
工具栏:
Executors 帮助创建各种线程池
ExecutorService newCachedThreadPool() 长度不限的线程池
ExecutorService newFixedThreadPool(int size) 长度固定的线程池
ExecutorService newSingleThreadExecutor() 单一的线程执行器
ScheduledExecutorService newScheduledThreadPool(int size) 长度固定可以调度的线程池
scheduleAtFixedRate(Runnable , 初始延迟时间,周期,时间单位)

注意:以上几种不推荐使用。就当我没写好了。。。

线程池的配置
ThreadPoolExecutor的构造方法

ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQue)

corePoolSize—核心线程数(线程池中最少保留的线程数)
maximumPoolSize—最大线程数
keepAliveTime—线程的闲置时间(非核心线程超过闲置时间不执行任务,就被删除)
unit—时间单位
workQueue—保存线程任务的阻塞队列(阻塞住线程,不让线程死亡)

优化配置:
1.核心线程数和最大线程数配置一样,不会频繁创建和销毁线程,效率高
2.线程数和CPU的核心数挂钩,线程数=CPU核心数*N(N>=1),N和系统吞吐量,用户量有关
3.闲置时间可以配置大一些,不会频繁创建和销毁线程
4.阻塞队列使用LinkedBlockingQueue性能比较高

4.ThreadLocal

因为今天遇到了mybatis线程同步问题,所以用到了ThreadLocal
SqlSessionFactory每次调用openSession()的方法创建sqlSession,执行增删改查的时候都会创建大量的sqlSession造成资源浪费。

解决方法:
1.将sqlSession定义为单独的对象。这样的话遇到多线程的环境会出现问题,因为sqlSession中封装的Connection不是线程安全的。
2.根据上面出现的问题,可以使用常见的上锁机制(以时间换空间)。但是这样会导致所有线程排队执行,效率低。
3.ThreadLocal线程局部变量(以空间换时间),为每个线程创建一个sqlSession绑定到线程上

这两者的区别就是,上锁统一访问sqlSessionFactory来最终创建sqlSession,最后每个线程的sqlSession派对执行。
ThreadLocal使得每个线程创建独立的sqlSession,相互不影响。

ThreadLocal的使用:

ThreadLocal<对象类型> threadLocal = new ThreadLocal<>();

将对象帮存在当前线程中

threadLocal.set(object)

获取当前线程中的对象

对象类型 demo  = threadLocal.get();

下面给出例子:

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MybatisUtil(){
	private static SqlSessionFactory factory = null;
	//使用ThreadLocal保存SqlSession的线程局部变量
	private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();
	static{
		//初始化工厂
		factory = new SqlSessionFactoryBuilder().build(
		MybatisUtil.class.getClassLoader().getResourceAsStream("mybatis.xml"));
	}
	/**
	*  获取sqlSession
	*/
	public static SqlSession getSqlSession(){
		 //获取当前线程中的sqlSession
		 SqlSession sqlSession = threadLocal.get();
		 //没有就创建
		 if(sqlSession == null){
			sqlSession = factory.openSession();
		 	threadLocal.set(sqlSession);
		 }
		 return sqlSession;
	}
	/**
	*  关闭sqlSession
	*/
	public static void closeSqlSession(){
		//获取当前线程中的sqlSession
		SqlSession sqlSession = threadLocal.get();
		//如果有就关闭这个会话
		if(sqlSession != null){
			sqlSession.close();
			sqlSession = null;
			//将该线程的局部变量sqlSession设置为null
			threadLocal.set(null);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值