多线程学习笔记:
synchronized关键字:既能够保证原子性也能够保证可见性。 被synchronized关键字修饰的对象会被加上一个互斥锁。
synchronized是一个非公平锁,如果多个线程处于等待状态,那么这些线程是随机获取锁的。
什么是互斥锁:同一时间只能被一个线程持有(注意:只有加了synchronized关键字同时会使用到这个对象的线程会呈现等待状态)
synchronized锁定一个静态对象时是直接锁定该对象的CLASS对象
synchronized所获得的锁是可以重入的:同一个class对象里面多个方法可以重复获得一个锁。
子类的同步方法可以调用父类的同步方法,允许这样。
线程出现异常的时候会直接释放锁,这时候在多线程情况下可能会读出错误数据
volatile关键字:线程之间可见性,禁止指jmm对计算机指令的重排序
一般来说线程读取某个对象都是将其放在自己的缓冲区,待对该对象操作完毕之后会将缓冲区更新后的数据写入堆里。
如果是加了volatile关键字,当某个线程对该对象做了修改之后会提示其他线程,从而更新其他线程缓冲区引用对象的值。
volatile关键字的效率比synchronized要高,volatile关键字是无锁同步的方式,但是不能够保证原子性(数据一致性)
atoMic类里面能够保证原子性,但是只能单一使用atoMic里面的方法是才具有原子性。多个方法使用时可能会出现问题。
synchronized: 细粒度的锁效率比粗粒度的锁效率高
new object() 是新建一个对象,所以不会存在锁被占用的问题。最好不要使用string 常量做synchronized的对象
wait() 将加锁的对象的当前使用线程置为等待状态,同时释放锁,此时其他线程可以调用此对象。唤醒线程的方法是notify和notifyall
注意: notify 和 notifyAll 不会释放锁
注意:notify 是唤醒沉睡的线程和对象无关。 wait是使当前线程沉睡,!!!!。虽然都是对象的方法,但一定注意的是它操作的是线程!!!
countdownlunch:门闩。不需要加锁也可实现沉睡和唤醒机制。
latch.await() 等待
latch.countdownl() 唤醒
reentratlock:手动锁可重入锁,使用完毕后必须手动释放。
// 获取reentratLock 实例
Lock lock = new reentratlock();
// 尝试获取锁
lock.tryLock(5,TimeUnit.SECCOND); 尝试获取锁(指定时间,指定时间单位)
// 设置线程可以被打断 , 设置了这个属性,主线程可以对该线程进行interrupt()打断
lock.lockInterruptibly();
// reentratLock可以设置公平锁,这样能够保证等待时间最久的线程获取线程
例子:
public class ReentratDemo extends Thread{
// 设置公平锁
private Lock lock = new ReentrantLock(true);
public void run(){
for (int i = 0; i < 100;i++){
try {
// 加上可重入锁
lock.lock();
System.out.println(Thread.currentThread().getName() + "获得锁");
}catch (Exception e){
System.out.println(e);
}finally {
// 释放锁:注意 reentratLock必须设置手动释放锁
lock.unlock();
}
}
}
public static void main(String[] args) {
ReentratDemo r = new ReentratDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
object.wait()方法通常是个while一起使用的;
while(size == max){ if(size == max){
try{ try{
this.wait() this.wait()
}catch(InterruntException e){ }catch(InterruntException e){
sout(e); sout(e);
} }
} }
forEach() forEach()
这里如果线程是被重新唤醒,还是会继续执行while中的判断 这里线程如果被唤醒,会直接执行wait()后面的代码,而不会再次去判断size == max,容易出现错误
synchronized 锁对象,可重入的互斥锁。抛出异常的时候会自动释放锁。
lock:reentratLock: 手动释放的可重入互斥锁。tryLck可以尝试获取锁,lockInterruntaby 设置可以被打断 new 的时候可以指定reentratLock为公平锁
wait() 被锁住的对象释放锁,并且使当前线程处于等待状态
notify/notifyAll 唤醒等待线程/唤醒等待全部的线程
Condotion condition = reenstratLock.newCondition(); condition可以精确地指定某些或者说某一个线程的沉睡和唤醒。
threadLocal:每一个线程设置的对象只能够提供给自己的线程使用。线程之间不恭喜。
session里面的设置就使用了threadLocal
单例模式(singleton):无论如何,线程池里面只有一个对象。分为懒汉模式(用到时才new),和饿汉模式(直接new)
concurrent包下的类:
concurrentQueue: 线程安全队列 add() 如果超过容器容量的最大值会抛出异常。
offer() 也是往容器里面加,有返回值boolean类型的,达到最大值的时候它便不能够往里面加了。
concurrentHashMap 和 HashTable 效率问题:concurrentHashMap效率更高。hashTable是锁住整个容器
concurrentHashMap1.7 锁的将容器分成一块一块的segament中的其中一个segament称之为分段锁。
concurrentHashMap1.8 锁的是其中的一个node,采用cas算法来插入或者修改数据。
在你put数据进入concurrentHashMap时,会使用cas算法在内存中计算出一个hash值,如果和你put的对象计算出的hash值一致,则允许写入,保证了原子性。
concurrentSkipListMap :线程安全的有序的map
copyOnWriteList() 写的时候会复制,写的少读得多的情况下使用
linkedBlockingQueue: 同步阻塞容器。 put() 当容器满了之后,线程会自动等待。 无界容器。
ArrayBlockingQueue:同步阻塞队列 。有界队列,默认有大小,自己也可以设置大小。
delayQueue:可以指定时间,等待时间长的最先执行。
transferQueue: 提供者直接将消息发送给对应的消费者,这样能够提高效率。但是只有transfer()方法会使消息阻塞。
runnable():运行的是run()方法,没有返回值、不能够抛出异常。
callable():运行的是call()方法,有返回值,可以抛出异常。
executors工具类可以创建线程池和task
executorService.submit() 只是将这些任务(task)放入线程池中,但还未真正的运行
// 创建线程池
ExecuteService service = Executors.newXXXthreadPool();
// 将创建好的的任务添加到线程池中,得到一个future类
Future future = service.submit(callable or implements);
// future.get() 执行指定线程
future.get()
线程池demo:
/**
* 计算1 - 200000 的质数
*/
public class Executor {
static int myCoreCpu = 4;
/**
* 自定义任务
*/
static class myTask implements Callable{
int begin;
int end;
myTask(int begin,int end){
this.begin = begin;
this.end = end;
}
@Override
public List<Integer> call() throws Exception {
return getPrise(begin,end);
}
}
static List<Integer> getPrise(int from,int end){
List<Integer> priseList = new ArrayList<>();
for(int i = from ; i <= end ;i ++){
boolean prise = isPrise(i);
if(prise) priseList.add(i);
}
return priseList;
}
static boolean isPrise(int num){
for(int i = 2; i < num/2; i++){
if(num % i == 0){
return false;
}
}
return true;
}
public static void main(String[] args) throws ExecutionException,InterruptedException{
List<Integer> l1 = new ArrayList<>();
List<Integer> l2 = new ArrayList<>();
/**
* 不使用线程池做任务调度
*/
long time = new Date().getTime();
for(int i = 1;i <= 200000;i++){
boolean prise = isPrise(i);
if (prise) l1.add(i);
}
long end = new Date().getTime();
System.out.println("数组大小:" + l1.size() + "-----用时:" + (end - time));
/**
* 线程池实现
*/
ExecutorService service = Executors.newFixedThreadPool(myCoreCpu);
myTask m1 = new myTask(1,80000);
myTask m2 = new myTask(80001,130000);
myTask m3 = new myTask(130001,170000);
myTask m4 = new myTask(170001,200000);
/**
* 将任务提交到线程池
*/
Future f1 = service.submit(m1);
Future f2 = service.submit(m2);
Future f3 = service.submit(m3);
Future f4 = service.submit(m4);
long startTime = new Date().getTime();
/**
* 启动线程
*/
l2.addAll ((List<Integer>)f1.get());
l2.addAll ((List<Integer>)f2.get());
l2.addAll ((List<Integer>)f3.get());
l2.addAll ((List<Integer>)f4.get());
long endTime = new Date().getTime();
System.out.println("数组大小:" + l2.size() + "------用时:" + (endTime - startTime));
}
/**
* 关闭service
*/
service.shutdown();
}
ExecutorService.submit --> Future --> Future.get();
catchThredPool --> 可以设置线程存活时间aliveTime:默认为60s
Executors工具类里面有很多种类型的threadPool,分别适用于不同的场景,详细的需要看api。
Executors的创建的线程池的底层是 return new ThreadPoolExecutor(int corePoolSise,
int maxPoolsize,long keepAliveTime,TimeUnit unit,WorkQueue linkedBlockingQueue ThreadFactory tf)
根据里面参数的值可以分为不同类型的线程池:
1.指定核心线程数线程池FixedThread:core = max = 指定参数,keepAliveTime = 0
特点:使用shoutDown()方法可以线程执行完毕之后自动关闭线程池。
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
2.单例线程数singletonThreadPool:core = max = 1,keepAliveTime = 0;
特点:线程池中只能有一条线程在执行,其他的线程在workQueue里面等待,能保证线程执行的顺序。
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
3.缓存线程池,cacheThreadPool: core = 0,max = integer.maxValue(),keepAliveTime = 60L(默认,可自己设定),TimeUnit.SECCOND ,workQueue = SynchronousQueue
SynchronousQueue 是线程安全的queue不经过线程池,直接被调用,如果没人调用就一直处于阻塞状态
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
4.定时线程:ScheduledThreadPoolExecutor 第三个参数为开启时间,workQueue = delayedWorkQueue;(优先级队列,先进先出)
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}