一、JUC的技术优势
1、提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
2、提高计算机系统CPU的利用率
3、改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
4、使用线程可以将耗时任务放到后台去处理,例如等待用户输入、文件读写和网络收发数据等。
二、多线程的安全
1、使用同步代码块解决
在同步代码块中,多个线程必须使用的是同一把锁,即同一个对象。这种方式类似于C语言设置一个mutex信号量的方式类似,其语法结构如下:
synchronized(互斥锁){
可能会发生线程冲突问题的代码块;
}
2、使用Lock解决线程安全
Lock实现提供更广泛的锁定操作可以比使用 synchronized获得方法和声明更好。他们允许更灵活的结构,可以有完全不同的特性,可以支持多个相关的 Condition对象。Lock提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。其中,ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
//创建锁
Lock lock = new ReentrantLock();
public void sale() {
//上锁
lock.lock();
try {
//执行代码块
}
} finally {
//解锁
lock.unlock();
}
}
}
三、多线程的两种创建方式
一、继承Thread类,重写run()方法,其具体步骤如下:
1、定义子类继承Thread类。
2、子类中重写Thread类中的run方法。
3、创建Thread子类对象,即创建了线程对象。
4、调用线程对象start方法启动线程,默认调用run方法。
二、实现runnable接口,重写run()方法,最后用Thread方法封装
1、定义子类,实现Runnable接口。
/2、子类中重写Runnable接口中的run方法。
3、通过Thread类含参构造器创建线程对象,将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法中。
4、调用Thread类的start方法启动线程,其最终调用Runnable子类接口的run方法。
四、JUC工具类
1、 ReentrantReadWriteLock类
现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。针对这种场景,JAVA的并发包提供了读写锁ReentrantReadWriteLock,它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁。当没有其他线程的写锁时,线程进入读锁。当没有其他线程的读锁和写锁时,才会进入当前线程的写锁!
2、CountDownLatch类
CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。
3、 CyclicBarrier类
CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。线程进入屏障通过CyclicBarrier的await()方法。
4. Semaphore类
在信号量上我们定义两种操作:
acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),要么一直等下去,直到有线程释放信号量,或超时。release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。