目录
1、线程和进程
进程:一个程序,QQ.exe
一个进程至少包含一个线程
java默认两个线程:main,GC
java开启线程的方式:Thread,Runable,Callable
java无法开线程
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//本地方法,底层的c++,java 无法直接操作
private native void start0();
并行,并发
并发编程:并发,并行
- 并发(多线程操作同一个资源)
- CPU一核,模拟出多条线程,快速交替
- 并行(多个人一起行走)
- CPU多核,多个线程可以同时执行;线程池
并发编程的本质:充分利用CPU资源
线程有几个状态
public enum State {
//新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待,死死的等
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
wait/sleep区别
1.来自不同的类
wait =》 Object
sleep =》Thread
2.关于锁的释放
wait会释放锁,sleep不释放
3.使用的范围是不同的
wait 必须在同步代码块中
sleep可以在如何地方
4.是否捕获异常
wait和sleep都需要捕获异常
public static void main(String[] args) throws InterruptedException {
Thread.sleep(1);
Thread thread = new Thread();
thread.wait();
}
2、Lock锁(重点)
传统Synchronize
Lock接口
默认非公平锁
非公平锁:十分不公平(可以插队)
公平锁:十分公平(3h 3s)
public class SaleTicketDemo02 {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
new Thread(()->{
for (int i = 0; i < 30; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 30; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 30; i++) {
ticket.sale();
}
},"C").start();
}
}
class Ticket2{
private int number=50;
Lock lock = new ReentrantLock();
public void sale(){
lock.lock(); //加锁
try {
if(number > 0){
System.out.println(Thread.currentThread().getName()+"卖出了 "+(50 - --number)+" 张票,剩余 "+number+" 张票");
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
Synchronize 和 Lock 区别
- Synchronize 内置的java关键字,Lock是一个java类
- Synchronize 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
- Synchronize 会自动释放锁,Lock必须要手动释放锁,如果不释放锁,死锁
- Synchronize 线程1(获取锁,阻塞),线程2(傻傻等待);Lock锁就不一定等待下去;
- Synchronize 可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己调节,默认false);
- Synchronize 适合锁少量的代码同步问题,Lock适合锁大量的同步代码块
可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁
可重入锁:对象获取锁后还可以再次获取该锁
锁是什么,如何判断锁的是谁?
3、生产者和消费者问题
面试:单例模式,排序算法,生产者和消费者,死锁
Synchronize wait notify
生产者和消费者 Synchronize版
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
//判断等待,业务,通知
class Data{ //数字 资源类
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if(number!=0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if(number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,-1完毕了
this.notifyAll();
}
}
但如果存在多个线程的话就会出问题
虚假唤醒
需要把 if 改为 while
Lock 锁版本
condition.await();//等待
condition.signalAll();//唤醒全部
和Synchronize版本的区别就是,类名前面的Synchronize变成了:lock.lock()和lock.unlock()
lock.lock();
try{
while(){ //用while可以防止虚假唤醒
等待
}
业务代码
唤醒其他线程
} catch(e){
}finally{
lock.unlock();
}
/**
* Lock锁
*/
public class B {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//判断等待,业务,通知
class Data2 { //数字 资源类
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// condition.await();//等待
// condition.signalAll();//唤醒全部
//+1
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
//等待
condition.await();
}
//业务代码
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程,+1完毕了
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//-1
public synchronized void decrement() throws InterruptedException {
lock.lock();
try {
while (number == 0) {
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程,-1完毕了
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition 为什么要使用它,而不使用
Condition可以精准的通知和唤醒线程
/**
* A执行完调用B
* B执行完调用C
* C执行完调用A
*/
public class C {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data3.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data3.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data3.printC();
}
},"C").start();
}
}
//资源类
class Data3{
private Lock lock = new ReentrantLock();
//三个监视器
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
private int number=1; // 1A 2B 3C
public void printA(){
lock.lock();
try {
//业务,判断 -》 执行 -》 通知
while(number!=1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAAA");
//唤醒指定线程
number=2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
//业务,判断 -》 执行 -》 通知
while(number!=2){
//等待
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBB");
//唤醒指定线程
number=3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
//业务,判断 -》 执行 -》 通知
while(number!=3){
//等待
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>CCCCC");
//唤醒指定线程
number=1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
4、8锁
1.Synchronize 锁方法 锁的是方法的调用者
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
//synchronize 锁的对象是方法的调用者
//两个方法用的是同一个锁,谁先拿到谁先执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call(){
System.out.println("call");
}
}
结果:
sendSms
call
2.当有两个调用者时,就不会锁,普通方法也不会锁
public class Test2 {
public static void main(String[] args) {
//两个调用对象
Phone2 phone = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
//普通方法
// phone.hello();
//不同对象,call和sendSms不会锁在一起
phone2.call();
},"B").start();
}
}
class Phone2{
//synchronize 锁的对象是方法的调用者
//两个方法用的是同一个锁,谁先拿到谁先执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call(){
System.out.println("call");
}
//普通方法没有锁
public void hello(){
System.out.println("hello");
}
}
结果:
call
sendSms
3.锁前面加static 锁的是Class,和调用者无关
public class Test3 {
public static void main(String[] args) {
Phone3 phone = new Phone3();
Phone3 phone2 = new Phone3();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone3{
//static 静态方法
//类一加载就有了,锁的对象是Class,不是调用者
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public static synchronized void call(){
System.out.println("call");
}
}
结果:
sendSms
call
4.Class的锁和普通方法的锁是不一样的
锁的东西不一样,相互不影响
public class Test4 {
public static void main(String[] args) {
Phone4 phone = new Phone4();
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone4{
//静态同步方法
//static 静态方法,锁的对象是Class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
//普通同步方法
public synchronized void call(){
System.out.println("call");
}
}
结果:
call
sendSms
小结:
Test1:synchronize方法 (同步锁)锁的调用者
Test2:普通方法和synchronize互不影响,同一调用者时也互不影响
Test3:static synchronize 锁的是Class对象
Test4:锁对象和锁调用者是互不影响的
5、集合类不安全
List Set 阻塞队列
List不安全
//ConcurrentModificationException
public class ListTest {
public static void main(String[] args) {
/**
* 并发下ArrayList不安全
*/
// List<String> list = Collections.synchronizedList(new ArrayList<>());
//写入时复制 多线程调用的时候
//在写入的时候避免覆盖造成数据问题
//读写分离 CopyOnWriteArrayList 比 Vector效率高
List<String> list = new CopyOnWriteArrayList<>();
//synchronize效率低
// List<String> list = new Vector<>();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
Set安全
public class SetTest {
public static void main(String[] args) {
// Set<String> set = new HashSet<>();
// Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 100; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
}).start();
}
}
}
HashSet 底层就是HashMap
Map不安全
ConcurrentHashMap
public class MapTest {
public static void main(String[] args) {
// Map<String, String> map = new HashMap<String, String>(16,0.75f);
//加载因子,初始化容量
// Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
}).start();
}
}
}
6、Callable(简单)
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
返回结果并可能引发异常任务。实现者定义一个没有参数的单一方法,称为call
Callable 类似 Runnable 因为它们都是为实例可能有另一个线程执行的类设计的。然而, A Runnable 不返回结果,也不能抛出被检查的异常
- 可以有返回值
- 可以抛出异常
- 方法不同,run() / call()
Runnable 的实现类 FutureTask类
FutureTask:
两个构造: Callable 的 和Runnable
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
Test:
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask futureTask = new FutureTask<>(myThread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();
String string = (String) futureTask.get();//Callable的返回值
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("111");
return "123";
}
}
结果只有打印一个 111
结果会被缓存,提高效率
细节:
- 有缓存
- 结果可以需要等待会阻塞
7、常用的辅助类
7.1、CountDownLatch
-
允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。
-
A CountDownLatch用给定的计数初始化。 await方法阻塞,直到由于countDown()方法的调用而导致当前计数达到零,之后所有等待线程被释放,并且任何后续的await 调用立即返回。 这是一个一次性的现象 - 计数无法重置。 如果您需要重置计数的版本,请考虑使用CyclicBarrier 。
-
A CountDownLatch是一种通用的同步工具,可用于多种用途。 一个CountDownLatch为一个计数的CountDownLatch用作一个简单的开/关锁存器,或者门:所有线程调用await在门口等待,直到被调用countDown()的线程打开。 一个CountDownLatch初始化N可以用来做一个线程等待,直到N个线程完成某项操作,或某些动作已经完成N次。
-
CountDownLatch一个有用的属性是,它不要求调用countDown线程等待计数到达零之前继续,它只是阻止任何线程通过await ,直到所有线程可以通过。
当计数为0,才能执行后面的close door
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" Go Out");
countDownLatch.countDown(); //数量-1
},""+i).start();
}
countDownLatch.await(); //等待计数器归零
System.out.println("close door");
}
}
7.2、CyclicBarrier
加法计数器
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(8,()->{
System.out.println("召唤成功!");
});
for (int i = 0; i < 7; i++) {
final int temp = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" 第"+temp+"颗龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},""+i).start();
}
}
}
7.3、Semaphore
//限流
public class SemaphoreDemo {
public static void main(String[] args) {
//线程数量
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(()->{
try {
semaphore.acquire(); // 获取得到
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release(); //release() 释放
}
},""+i).start();
}
}
}
semaphore.acquire();
semaphore.release(); //release() 释放
作用:多个共享资源互斥的使用!并发限流,控制最大的线程数