yield方法,意思是使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。
当多线程的情况下,遇到共享变量的情况下,为了保持数据的一致性,可以采用synchronized关键字,可以加方法上,表示对这个对象this加锁,但是效率不高,可以用synchronized代码段,只对共享变量的地方加锁,还有如果共享变量是数字时,用Atomic关键字修饰变量。可以不用synchronized。这个对象是java并发包给我提供的类,里面包含有加减运算
volatile关键字 ,cpu是有缓存的,当变量的值已经改变,但是cpu还是从缓存中取值就会出现错误,volatile关键字就是告诉cpu必须从内存中取值,不走缓存,volatile可以实现可见性,不能实现原子性问题。 不能解决并发问题
synchronize可以实现可见性和原子性问题。 可以解决并发问题
ThreadLocal 作用是和当前线程绑定,这个对象有个get,set方法,可以把对象放进去,不会出现多线程争夺共享变量的情况
同步容器: vector hashtable Collections.synchronize() 这些是一些老的api,源码基本上都是采用synchronize来实现的,很笨重。
**并发容器:**ConcurrentHashMap 分段锁 java 1.5提供的api
ConcurrentHashMap map = new ConcurrentHashMap ();
map .putIfAbsent(1,2) 这个方法是 如果key不存在就放进去,存在就返回none。
CopyOnWriteArrayList /Set 这两个实现的基本原理是操作的时候先上一把锁,然后把共享资源复制一份,然后再复制的资源里面搞搞搞,使用的场景是读的多,写的少的操作
BlocakingQueue 阻塞队列。有两个方法,一个put放东西,take取东西 队列特点就是先进先出,就像一根水管,往里面塞东西。先进的肯定先出。
闭锁 栅栏 信号量
闭锁的意思就是当线程都执行完了,才放行,如何实现呢?CountDownLatch 倒计时的意思
CountDownLatch latch = new CountDownLatch (2); //表示有两个线程
latch.countDown() //一般线程执行完之后调用这个方法,表示这个线程执行完了
latch.awit() //这个方法必须是latch里面的线程都执行完了,才放行。
栅栏 的相当于水坝的意思,就是有个线程达到某种状态之后,然后这个线程才能继续操作
CyclicBarrier barrier = new CyclicBarrier (4)
barrier.await() //只有当wait的线程等于4,才会继续往下走。不然都得等着。
信号量 生产者和消费者人数不对等的情况下,比如5个机器,8个人要用,刚开始每人一个,然后有人释放掉,另外一个工人就补上、
int t=8 //8个工人
Semaphore semaphore = new Semaphore(5) //机器的数目
semaphore.acquire(); //意思是从5台机器中获取一个
semaphore.release(); //意思是机器释放,下一个工人就顶上。
线程池
当遇到并发时,肯定需要用到线程池,因为你不可能没来一次请求就创建一个线程吧,那样的话你服务器会受不了的。
Executor executor = Executors.newFixedThreadPool(100);//意思是这个线程池有100个线程
executor.execute(线程) //把线程扔进去,就可以了。他就执行线程的start方法。
Executor的实现类ExecutorService 这个类是Executor的实现类,里面有几个非常常用的方法。
定时任务
java提供的Timer这个类可以实现定时任务。
注意:timer有两个缺点,一个是当有多个任务的时候,比如原本第二个任务是1秒之后执行,但是第一个任务执行两秒才结束,那么第二个任务就必须等第一个任务执行完再执行。
还有一个就是当第一个任务报错之后,timer里面的任务全都挂掉了。这是非常严重的后果。
Timer timer=new Timer();
timer.schedule(线程,1000);//意思是1000毫秒之后执行线程
//如何解决这个问题呢?
//创建一个可调度的线程池
ScheduledExecutorService ExecutorService = Executors.newScheduleduledThreadPool(100);
EsxecutorService.schedule(线程,1000,TimerUnit.MILLISECONDS)//最后一个是指明1000的时间单位,这里是毫秒
死锁
嵌套锁会造成死锁
解决办法:1避免嵌套锁,2嵌套锁的顺序,3引入超时机制
Lock锁
是java1.5提供的一种锁,可以很灵活的控制。
Lock lock=new ReentrantLock(); //创建一个可重入锁
//主要有两个方法
lock.lock(); //加锁
locak.unlock(); //解锁
ReentrantReadWriteLock rwl= new ReentrantReadWriteLock() ; //创建一个既可以读又可以写的锁
rwl.writeLock().lock() //创建一个写锁, 只能一个线程进来
rwl.writeLock().unlock() //释放写锁
rwl.readLock().lock() //创建一个读锁, 可以有多个线程进来
rwl.readLock().unlock() //释放读锁
悲观锁和乐观锁 —–数据库层面的
悲观锁:只有一个线程在操作,不管读还是写,直接把这个表锁住了,其他线程进不来,实现语句
后面加for update
select * from user for update
乐观锁:表中加version字段。
分布式锁:redis zk