理论:第八章:线程是什么,有几种实现方式,它们之间的区别是什么,线程池实现原理,JUC并发包,ThreadLocal与Lock和Synchronize区别
什么是线程?讲个故事给你听,让你没法去背这个题,地址:https://blog.csdn.net/java_wxid/article/details/94131223
有几种实现方式?
继承Thread类
实现Runnable接口
实现Callable接口
线程池方式
优缺点
1.继承Thread类
优点 、代码简单 。
缺点 、该类无法集成别的类。
2.实现Runnable接口
优点 、继承其他类。 同一实现该接口的实例可以共享资源。
缺点 、代码复杂
3.实现Callable
优点 、可以获得异步任务的返回值
4.线程池 、实现自动化装配,易于管理,循环利用资源。
代码实现案例:
继承Thread类,并重写里面的run方法
class A extends Thread{
public void run(){
for(int i=1;i<=100;i++){
System.out.println("-----------------"+i);
}
}
}
A a = new A();
a.start();
实现Runnable接口,并实现里面的run方法
class B implements Runnable{
public void run(){
for(int i=1;i<=100;i++){
System.out.println("-----------------"+i);
}
}
}
B b = new B();
Thread t = new Thread(b);
t.start();
实现Callable
class A implements Callable<String>{
public String call() throws Exception{
//...
}
}
FutureTask<String> ft = new FutureTask<>(new A());
new Thread(ft).start();
线程池
ExcutorService es = Executors.newFixedThreadPool(10);
es.submit(new Runnable(){//任务});
es.submit(new Runnable(){//任务});
...
es.shutdown();
问题扩展
在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?
整体上来说Lock是synchronized的扩展版,Lock提供了无条件的、可轮询的(tryLock方法)、定时的(tryLock带参方法)、可中断的(lockInterruptibly)、可多条件队列的(newCondition方法)锁操作。另外Lock的实现类基本都支持非公平锁(默认)和公平锁,synchronized只支持非公平锁,当然,在大部分情况下,非公平锁是高效的选择。
线程池的实现原理:https://blog.csdn.net/java_wxid/article/details/101844786
JUC并发包:
volatile的三大特性:https://blog.csdn.net/java_wxid/article/details/97611028
CompareAndSwap底层原理:https://blog.csdn.net/java_wxid/article/details/97611037
AtomicReference原子引用:https://blog.csdn.net/java_wxid/article/details/97611046
CountDownLatch倒计时器:https://blog.csdn.net/java_wxid/article/details/99168098
CyclicBarrier循环栅栏:https://blog.csdn.net/java_wxid/article/details/99171155
Semaphore信号灯:https://blog.csdn.net/java_wxid/article/details/99174538
ThreadLocal与Lock和Synchronize区别
ThreadLocal与Lock和Synchronize区别
ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。ThreadLocal采用了“以空间换时间”的方式,为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。同步机制采用了“以时间换空间”的方式,仅提供一份变量,让不同的线程排队访问。
如果一个代码块被synchronized关键字修饰,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待直至占有锁的线程释放锁。事实上,占有锁的线程释放锁一般会是以下三种情况之一:
占有锁的线程执行完了该代码块,然后释放对锁的占有;
占有锁线程执行发生异常,此时JVM会让线程自动释放锁;
占有锁线程进入 WAITING 状态从而释放锁,例如在该线程中调用wait()方法等。
synchronized 是Java语言的内置特性,可以轻松实现对临界资源的同步互斥访问。那么,为什么还会出现Lock呢?试考虑以下三种情况:
Case 1 :
在使用synchronized关键字的情形下,假如占有锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,那么其他线程就只能一直等待,别无他法。这会极大影响程序执行效率。因此,就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间 (解决方案:tryLock(long time, TimeUnit unit)) 或者 能够响应中断 (解决方案:lockInterruptibly())),这种情况可以通过 Lock 解决。
Case 2 :
我们知道,当多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作也会发生冲突现象,但是读操作和读操作不会发生冲突现象。但是如果采用synchronized关键字实现同步的话,就会导致一个问题,即当多个线程都只是进行读操作时,也只有一个线程在可以进行读操作,其他线程只能等待锁的释放而无法进行读操作。因此,需要一种机制来使得当多个线程都只是进行读操作时,线程之间不会发生冲突。同样地,Lock也可以解决这种情况 (解决方案:ReentrantReadWriteLock) 。
Case 3 :
我们可以通过Lock得知线程有没有成功获取到锁 (解决方案:ReentrantLock) ,但这个是synchronized无法办到的。
上面提到的三种情形,我们都可以通过Lock来解决,但 synchronized 关键字却无能为力。事实上,Lock 是 java.util.concurrent.locks包 下的接口,Lock 实现提供了比 synchronized 关键字 更广泛的锁操作,它能以更优雅的方式处理线程同步问题。也就是说,Lock提供了比synchronized更多的功能。但是要注意以下几点:
1)synchronized是Java的关键字,因此是Java的内置特性,是基于JVM层面实现的。而Lock是一个Java接口,是基于JDK层面实现的,通过这个接口可以实现同步访问;
2)采用synchronized方式不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而 Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致死锁现象。
关于读写锁:https://blog.csdn.net/java_wxid/article/details/99165717