进程:进行(执行)中的应用程序,我们称之为进程。进程属于CPU分配资源的最小单位。
目前操作系统都是支持多进程的,可以同时支持多个进程,通过进程ID区分。
单核CPU在同一时刻,只能运行一个进程;宏观并行丶微观串行
线程:线程属于CPU执行调度的最小单位。又称轻量级进程(Light Weight Process)。进程中的一条执行路径,也是CPU的基本调度单位,一个进程有一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程。
Java虚拟机是一个进程,当中包含主线程(main),可通过代码创建多个独立线程,与main并发执行。
- 进程是操作系统资源分配的基本单位,而线程是CPU调度的基本单位
- 一个程序运行后至少有一个进程
- 一个进程包含一个或多个线程,
- 进程间不能共享数据段地址,但同进程的线程之间可以,
任何一个线程都具有基本的组成部分:
- CPU时间片:操作系统(OS)会为每个线程分配执行时间。
运行数据:
- 堆空间:存储线程需使用的对象,多个线程可以共享堆中的对象
- 栈空间:存储线程需要使用的局部变量,每个线程都拥有独立的栈
- 线程抢占式执行
- 效率高
- 可防止单一线程长时间独占CPU
- 在单核CPU中,宏观上同时执行,微观上顺序执行。
并发:同时发生,轮流交替来执行。
并行:真正意义上的同时执行。
/***
* 继承Thread类
* this.getId() 获取当前线程的id
* this.getName() 获取当前线程的名字
* 注意:只能在继承Thread类时才能使用
* Thread.currentThread().getId() 获取当前线程的id
* Thread.currentThread().getName() 获取当前线程的名字
* start() 开启线程
* ░ ░ ░ ░
*/
class MyThreadTest extends Thread{
@Override
public void run() {
for(int i =0;i<100;i++){
System.out.println("线程id:" + this.getId() +"--------" + "线程名字:" + this.getName() + "子线程:----" + i);
}
}
}
public class MyThread {
public static void main(String[] args) {
MyThreadTest th1 = new MyThreadTest();
// 开始线程
th1.start();
// 主线程main执行
for(int i = 0;i<50;i++){
System.out.println("线程id:" + Thread.currentThread().getId() +"--------" + "线程名字:" + Thread.currentThread().getName());
}
}
}
/*
* 修改线程名字
* 方式1 调用setName()直接修改线程名字
* 方式2 构造方法修改线程名字
* */
// 方式1
class MyThreadTest extends Thread{
@Override
public void run() {
for(int i =0;i<100;i++){
System.out.println("线程id:" + this.getId() +"--------" + "线程名字:" + this.getName() + "子线程:----" + i);
}
}
}
public class MyThread {
public static void main(String[] args) {
MyThreadTest th1 = new MyThreadTest();
// 方式一 修改线程名字
th1.setName("我是一个子线程");
// 开始线程
th1.start();
}
}
// =========================================
// 方式2
class MyThreadTest extends Thread{
// 方式二
public MyThreadTest(){}
// 在构造方法中传递给Thread类一个name
public MyThreadTest(String name){super(name);}
@Override
public void run() {
for(int i =0;i<100;i++){
System.out.println("线程id:" + this.getId() +"--------" + "线程名字:" + this.getName() + "子线程:----" + i);
}
}
}
public class MyThread {
public static void main(String[] args) {
MyThreadTest th1 = new MyThreadTest("我是一个子线程");
// 开始线程
th1.start();
}
}
public class TicketWin extends Thread{
private int ticket = 100;
public TicketWin(){}
public TicketWin(String name){super(name);}
@Override
public void run() {
while(true){
if(ticket <= 0 ){
break;
}else{
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票" ) ;
ticket--;
}
}
}
public static void main(String[] args) {
TicketWin t1 = new TicketWin("子线程1");
TicketWin t2 = new TicketWin("子线程2");
TicketWin t3 = new TicketWin("子线程3");
TicketWin t4 = new TicketWin("子线程4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i =0;i<100;i++){
System.out.println("线程名字:"+Thread.currentThread().getName()+"---"+i);
}
}
public static void main(String[] args) {
// 创建Runnable对象,表示线程要执行的功能
MyRunnable myRunnable = new MyRunnable();
// 创建线程对象 Thread可以传入一个Runnable实现类和线程名字
Thread th1 = new Thread(myRunnable,"子线程");
th1.stat();
}
}
public static void main(String[] args) {
// 使用匿名内部类创建Runnable
Runnable runnable = new Runnable(){
@Override
public void run() {
for(int i =0;i<100;i++){
System.out.println("线程名字:"+Thread.currentThread().getName()+"---"+i);
}
}
};
// 创建线程对象
Thread th1 = new Thread(runnable,"runnable线程");
th1.start();
}
线程状态:
- new Thread() 线程对象被创建 即为 初始状态 只在堆中开辟内存,与常规对象无异
- start() 就绪状态
- run() 运行run()方法 运行状态
- sleep join … 阻塞
- 终止线程 结束
sleep():
每过多久就会让线程休息一会,然后再执行
Runnable runnable = new Runnable(){
@Override
public void run() {
for(int i =0;i<10;i++){
System.out.println("线程名称" + Thread.currentThread().getName() + "-----" +i);
try {
// 线程休眠
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(runnable,"牛牛").start();
Yield():
线程与线程之间相互礼让 轮流交替执行
Runnable runnable = new Runnable(){
@Override
public void run() {
for(int i =0;i<10;i++){
System.out.println("线程名称" + Thread.currentThread().getName() + "-----" +i);
// 线程礼让
Thread.yield();
}
}
};
new Thread(runnable,"熊大").start();
new Thread(runnable,"熊二").start();
join():
加入到当前线程,并阻塞当前的线程,直到加入的线程执行完毕
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable(){
@Override
public void run() {
for(int i =0;i<10;i++){
System.out.println("线程名称" + Thread.currentThread().getName() + "-----" +i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread th = new Thread(runnable, "熊二");
th.start();
// join 加入到当前线程 并阻塞当前线程 直到加入线程执行完毕
th.join();
for(int i =0;i<10;i++){
System.out.println("线程名称" + Thread.currentThread().getName() + "-----" +i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
setPriority() :
线程优先级为1-10,默认5 ,优先级越高,表示获取CPU机会越多
// 设置线程优先级:
Runnable r1 = new Runnable() {
@Override
public void run() {
for(int i =0;i<10;i++){
System.out.println("线程名称" + Thread.currentThread().getName() + "-----" +i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t1 = new Thread(r1,"熊大");
Thread t2 = new Thread(r1,"熊二");
t1.start();
t2.start();
t1.setPriority(1);
t2.setPriority(10);
守护线程:
- 线程对象.setDaemon(true)设置为守护线程
- 线程有两类:用户线程(前台线程)丶守护线程(后台线程)
- 如果程序中所有前台线程都执行完毕了,后台线程会自动结束
- 垃圾回收器线程属于守护线程
Runnable runnable = new Runnable() {
@Override
public void run() {
for(int i =0;i<30;i++){
System.out.println("线程名称" + Thread.currentThread().getName() + "-----" +i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread th = new Thread(runnable,"前台线程");
// 设置为守护线程
th.setDaemon(true);
th.start();
for(int i =0;i<10;i++){
System.out.println("线程名称" + Thread.currentThread().getName() + "-----" +i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
线程安全问题:
- 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
- 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。
- 原子操作:不可分割的多不操作,被视作一个整体,其顺序和步骤不可打乱或缺省。
synchronized(临界资源){ // 对临界资源对象加锁
// 代码 (原子操作)
}
为当前线程上锁,即使当前线程时间片结束,还没执行完毕,下个线程也不会破坏当前线程所执行的结果
// 创建数组
String[] s = new String[5];
// 创建线程
Runnable r1 = new Runnable() {
@Override
public void run() {
synchronized(s){
s[index] = "Hello";
index++;
}
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
synchronized(s){
s[index] = "World";
index++;
}
}
};
Thread th1 = new Thread(r1,"A");
Thread th2 = new Thread(r2,"B");
th1.start();
th2.start();
th1.join();
th2.join();
System.out.println(Arrays.toString(s));
为当前方法加锁,synchronized 关键字加载返回值之前。锁是this
修饰符 synchronized 返回值 方法名(){
// 代码(原子操作)
}
// 实现Runnable接口
Runnable runnable = new Runnable() {
private int ticket = 100;
@Override
public void run() {
while(true){
if(!sale()){
break;
}
}
}
// 同步方法 synchronized 加在返回值之前
// 同步方法 的锁就是this 谁调用指向谁
public synchronized boolean sale(){
synchronized(this){
if(ticket <= 0 ){
return false;
}else{
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票" ) ;
ticket--;
}
}
return true;
}
};
// 创建线程对象
Thread th1 = new Thread(runnable,"线程1");
Thread th2 = new Thread(runnable,"线程1");
Thread th3 = new Thread(runnable,"线程1");
Thread th4 = new Thread(runnable,"线程1");
th1.start();
th2.start();
th3.start();
th4.start();
为当前方法加锁,synchronized 关键字加载返回值之前。锁是this
public class Tickets implements Runnable{
private static int ticket = 100;
@Override
public void run() {
while(true){
if (!sale()) {
break;
}
}
}
// 同步静态方法 关键字加载静态方法里面 锁是 类名.class
public static boolean sale(){
synchronized(Tickets.class){
if(ticket<=0){
return false;
}else{
System.out.println(Thread.currentThread().getName()+"买了第" + ticket + "票");
ticket--;
return true;
}
}
}
public static void main(String[] args) {
Tickets tk = new Tickets();
Thread th = new Thread(tk,"熊二");
th.start();
}
}
同步规则:
- 只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。
- 如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用。
死锁:
- 当一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象的锁标记,并等待A对象锁标记时,产生死锁。
- 一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。
public class MyLock {
// 创建两个锁
public static Object a = new Object();
public static Object b = new Object();
public static void main(String[] args) throws InterruptedException {
Boy boy = new Boy();
Girl girl = new Girl();
boy.start();
Thread.sleep(300);
girl.start();
}
}
class Boy extends Thread{
@Override
public void run() {
synchronized (MyLock.a){
System.out.println("男孩拿到了a锁");
synchronized (MyLock.b){
System.out.println("男孩拿到了b锁");
System.out.println("男孩开始吃东西");
}
}
}
}
class Girl extends Thread{
@Override
public void run() {
synchronized (MyLock.b){
System.out.println("女孩拿到了b锁");
synchronized (MyLock.a){
System.out.println("女孩拿到了a锁");
System.out.println("女孩开始吃东西");
}
}
}
}
等待:锁.wait()
- public final void wait()
- public final void wait(long timeout)
- 必须在对obj加锁的同步代码块中,在一个线程中,调用obj.wait()时,此线程会释放其拥有的锁标记,同时此线程阻塞在o的等待队列,释放锁 ,进入等待队列
通知 唤醒:锁.notify()
- public final void notify()
- public final void notifyAll()
public class BankCard {
// 余额
private double money;
// 标记
private boolean flag = false; // 控制能否存取钱
// 存钱
public synchronized void save(double m){
while(flag){ // 如果flag 为true那么就不能存钱
try {
// 进入等待队列 释放锁和cpu
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
money = money + m;
System.out.println(Thread.currentThread().getName() + "存了" + m + "余额是" +money);
// 修改标记
flag = true;
// 唤醒取钱线程 锁.notify()
this.notifyAll();
}
// 取钱
public synchronized void take(double m){
while(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
money = money - m;
System.out.println(Thread.currentThread().getName() + "取了" + m + "余额是" +money);
// 修改标记
flag = false;
// 唤醒线程
this.notifyAll();
}
public static void main(String[] args) {
// 创建银行卡
BankCard card = new BankCard();
// 创建操作
AddMoney add = new AddMoney(card);
SubMoney sub = new SubMoney(card);
// 创建线程
Thread xiongda = new Thread(add,"熊大");
Thread xionger = new Thread(sub,"熊二");
xiongda.start();
try {
xiongda.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
xionger.start();
}
}
class AddMoney implements Runnable{
private BankCard card;
public AddMoney(BankCard card){
this.card = card;
}
@Override
public void run() {
for(int i=0;i<10;i++){
card.save(1000);
}
}
}
class SubMoney implements Runnable{
private BankCard card;
public SubMoney(BankCard card){
this.card = card;
}
@Override
public void run() {
for(int i=0;i<10;i++){
card.take(1000);
}
}
}
生产者丶消费者:
- 若干个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者都能并发执行,在两者间设置一个能存储多个产品的缓存区,生产者将生产的产品放入缓冲区,消费者从缓冲区取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区取走产品,也不允许生产者向一个缓冲区放入产品。
public class Bread {
private int id;
private String prodcutName;
@Override
public String toString() {
return "Bread{" +
"id=" + id +
", prodcutName='" + prodcutName + '\'' +
'}';
}
public Bread() {
}
public Bread(int id, String prodcutName) {
this.id = id;
this.prodcutName = prodcutName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProdcutName() {
return prodcutName;
}
public void setProdcutName(String prodcutName) {
this.prodcutName = prodcutName;
}
}
public class BreadCon {
// 创建数组
private Bread[] cons = new Bread[6];
// 存放面包的位置
private int index = 0;
// 存放面包
public synchronized void input(Bread b){ // 锁 this
// 判断容器是否满了
while(index >= 6){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
cons[index] = b;
System.out.println(Thread.currentThread().getName()+"生产了" + b.getId()+"");
index++;
this.notifyAll();
}
// 取出面包
public synchronized void output(){ // 锁 this
while(index <= 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index--;
Bread b = cons[index];
System.out.println(Thread.currentThread().getName()+"消费了" + b.getId()+"");
cons[index] = null;
// 唤醒生产者
this.notifyAll();
}
}
public class Product implements Runnable{
private BreadCon con;
public Product(BreadCon con) {
this.con = con;
}
@Override
public void run() {
for(int i=0;i<30;i++){
con.input(new Bread(i,Thread.currentThread().getName() ));
}
}
}
public class Cousume implements Runnable{
private BreadCon con;
public Cousume(BreadCon con) {
this.con = con;
}
@Override
public void run() {
for(int i=0;i<30;i++){
con.output();
}
}
}
public static void main(String[] args) {
// 容器
BreadCon breadCon = new BreadCon();
// 生产者和消费者
Product product = new Product(breadCon);
Cousume cousume = new Cousume(breadCon);
// 创建线程对象
Thread xiongda = new Thread(product,"熊大");
Thread xionger = new Thread(cousume,"熊二");
xiongda.start();
xionger.start();
}
问题:
- 线程是宝贵的内存资源,单个线程约占1MB空间,过多分配易造成内存溢出,
- 频繁的创建及销毁线程会增加虚拟机回收频率,资源开销,造成程序性能下降。
线程池:
- 线程容器,可设定线程分配的数量上限。
- 将预先创建的线程对象存入池中,并重用线程池中的线程对象。
- 避免频繁的创建和销毁。
将任务提交给线程池,由线程池分配线程,运行任务,并在当前任务结束后复用线程。
常用的线程池接口和类(所在包java,util.concurrent):
- Executor: 线程池的顶级接口
- ExecutorService : 线程池接口,可通过submit(Runnable task)提交任务代码。
- Executors工厂类:通过此类可以获得一个线程池。
- 通过 newFixedThreadPool(int nThreads) 获取固定数量的线程池。参数:指定线程池中线程的数量。
- 通过newCachedThreadPool() 获得动态数量的线程池,如果不够则创建新的,没有上限。
/*
* 线程池创建
* Executor : 线程池的根接口, Executor()
* ExecutorService : 包含管理线程池的一些方法,Submit() 提交线程任务 shutdown()关闭线程池
* ThreadPoolExecutor
* ScheduledThreadPoolExecutor
* Executors : 创建线程池的工具类
* (1) 创建固定线程个数的线程池
* (2) 创建缓存线程池,由任务的多少决定
* (3) 创建单线程池
* (4) 创建调度线程池, 调度 : 周期 定时执行
*
* */
public class Executorss {
public static void main(String[] args) {
// 创建固定个数的线程池对象
ExecutorService es = Executors.newFixedThreadPool(4);
// 创建缓存线程池,线程个数由任务多少决定
// ExecutorService es = Executors.newCachedThreadPool();
// 创建单线程池 不常用
// ExecutorService es = Executors.newSingleThreadExecutor();
// 创建调度线程池, 调度 : 周期 定时执行
// ScheduledExecutorService es = Executors.newScheduledThreadPool(3);
// 创建线程任务
Runnable runnable = new Runnable() {
private int ticket = 100;
@Override
public void run() {
while(true){
if(ticket <= 0){
break;
}
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票。");
ticket--;
}
}
};
// 提交线程任务
for(int i=0;i<5;i++){
es.submit(runnable);
}
// 关闭线程池
// shutdown()等待线程池任务执行完毕后关闭线程池
// shutdownNow()直接关闭线程池不管任务是否执行完毕
es.shutdown();
}
}
Callable:
- JDK5加入,与Runnable接口类似,实现之后代表一个线程任务。
- Callable具有泛型返回值丶可以声明异常。
/*
* Callable 接口
* Callable 和 Runnable区别
* (1) Callable接口中call方法有返回值,Runnable接口中run方法没有返回值
* (2) Callable接口中call方法有声异常,Runnable接口中run方法没有异常
* */
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// Callable接口创建 它是一个接口 用匿名内部类创建
Callable<Integer> callable = new Callable() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "开始计算1-100和");
int sum = 0;
for(int i = 0;i<=100;i++){
sum += i;
}
return sum;
}
};
// 把Callable接口转换成一个任务
// FutureTask 实际上继承了Runnable
FutureTask<Integer> task = new FutureTask(callable);
// 创建线程对象
Thread th = new Thread(task);
// 启动线程
th.start();
// get() 获取Callable的返回值
// 等call方法执行完毕 get()才能获取到结果
Integer result = task.get();
System.out.println(result);
}
}
// Callable接口配合线程池使用
public class CallableExecutor {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建线程池
ExecutorService es = Executors.newFixedThreadPool(1);
// 提交任务 把Callable提交给线程池
// Future 表示将要执行完任务的结果
Future<Integer> future = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "开始计算1-100和");
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
});
// 获取线程任务执行完的结果
System.out.println(future.get());
// 关闭线程池
es.shutdown();
}
}
Future : 表示将要完成线程任务的结果。 等待线程任务执行完毕。
Future : 表示ExecutorService.submit() 所返回的状态结果,就是call()的返回值。
方法:V get() 以阻塞形式等待Future中的异步处理结果(call()的返回值)
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建线程池
ExecutorService es = Executors.newFixedThreadPool(2);
// 提交任务
Future<Integer> future1 = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// 计算1-50的和
int sum = 0;
System.out.println(Thread.currentThread().getName()+"计算1-50的和计算完毕");
for(int i=0;i<=50;i++){
sum += i;
}
return sum;
}
});
Future<Integer> future2 = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// 计算51-100的和
int sum = 0;
System.out.println(Thread.currentThread().getName()+"计算51-100的和计算完毕");
for(int i=51;i<=100;i++){
sum += i;
}
return sum;
}
});
// 获取结果
Integer sum = future1.get() + future2.get();
System.out.println(sum);
// 关闭线程池
es.shutdown();
}
}
同步:
- 形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续。
异步:
- 形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回,而这竞争时间片,并发执行。
- 异步是多条执行路径
- JDK5加入,与synchronized比较,显示定义,结构更灵活。
- 提供更多实用性方法,功能更强大,性能更优越。
- 常用方法:
- void lock() 获取锁,如果锁被占用,则等待。
- boolean tryLock() 尝试获取锁(成功返回true,失败返回false,不阻塞)
- void unlock() 释放锁
ReentrantLock : Lock接口的实现类,与 synchronized 一样具有互斥锁功能。
// ReentrantLock
public class ReentrantLockTest {
// 创建 ReentrantLock
private Lock lock = new ReentrantLock();
private String[] str = {"A","B","","",""};
private int count = 2;
public void add(String value){
// 上锁
lock.lock();
try{
str[count] = value;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
System.out.println(Thread.currentThread().getName() + "添加了" + value);
}finally {
// 释放锁
lock.unlock();
}
}
public String[] getStr(){
return str;
}
// 测试
public static void main(String[] args) throws InterruptedException {
// 实例化ReentrantLockTest类
ReentrantLockTest rl = new ReentrantLockTest();
// 实现Runnable
Runnable runnable1 = new Runnable() {
@Override
public void run() {
rl.add("Hello");
}
};
Runnable runnable2 = new Runnable() {
@Override
public void run() {
rl.add("World");
}
};
// 创建线程对象
Thread th1 = new Thread(runnable1,"熊大");
Thread th2 = new Thread(runnable2,"熊二");
// 启动线程
th1.start();
th2.start();
th1.join();
th2.join();
System.out.println(Arrays.toString(rl.getStr()));
}
}
// 创建线程池
ExecutorService es = Executors.newFixedThreadPool(4);
// 提交线程任务
for(int i=0;i<4;i++){
es.submit( // 实现Runnable匿名内部类
new Runnable(){
private int ticket = 100;
// 创建 ReentrantLock
private Lock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try{
// 上锁
lock.lock();
if(ticket <= 0){
break;
}
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票");
ticket--;
}finally {
// 释放锁
lock.unlock();
}
}
}
});
}
// 关闭线程池
es.shutdown();
ReentrantReadWriteLock :
- 一种支持一写多读的同步锁,读写分离,可分别分配读锁,写锁。
- 支持多次分配读锁,使多个操作可以并发执行。
// 读写锁
public class ReadWriteLockTest {
// 创建读写锁
private ReentrantReadWriteLock rrl = new ReentrantReadWriteLock();
// 创建读锁
private ReentrantReadWriteLock.ReadLock readLock = rrl.readLock();
// 创建写锁
private ReentrantReadWriteLock.WriteLock writeLock = rrl.writeLock();
private String value;
public String getValue(){
// 使用读锁 上锁
readLock.lock();
try{
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("读取:" + value);
return this.value;
}finally {
// 释放读锁
readLock.unlock();
}
}
public void setValue(String value){
// 使用写锁 上锁
writeLock.lock();
try{
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("写入:" + value);
this.value = value;
}finally {
writeLock.unlock();
}
}
}
class TestReadWriteLock{
public static void main(String[] args) {
ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
// 创建线程池
ExecutorService es = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis();
// 提交写入任务线程
for(int i=0;i<2;i++){
es.submit(new Runnable() {
@Override
public void run() {
readWriteLockTest.setValue("牛小牛");
}
});
}
// 提交读取任务线程
for(int i=0;i<18;i++){
es.submit(new Runnable() {
@Override
public void run() {
readWriteLockTest.getValue() ;
}
});
}
// 关闭线程池
es.shutdown();
// 判断线程任务是否执行完毕 线程池是否关闭
while(!es.isTerminated()){ // 空转
}
long end = System.currentTimeMillis();
System.out.println("用时:" + (end - start));
}
}
CopyOnWriteArray:
- 线程安全的ArrayList,加强版的读写分离。
- 写有锁,读无锁,读写之间不阻塞,优于读写锁。
- 写入时(添加) , 先copy一个容器副本,再添加新元素,最后替换引用。
- 使用方式 与ArrayList无异。
// 创建 CopyOnWriteArrayList
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 多线程操作集合
ExecutorService es = Executors.newFixedThreadPool(5);
// 提交任务
for(int i=0;i<5;i++){
es.submit(new Runnable() {
@Override
public void run() {
for(int j = 0;j < 10;j++){
list.add(Thread.currentThread().getName() +"..." + new Random().nextInt(1000));
}
}
});
}
// 关闭线程池
es.shutdown();
while (!es.isTerminated()){}
// 打印结果
System.out.println("元素个数:" + list.size());
for (String s : list) {
System.out.println(s);
}
CopyOnWriteArraySet:
- 线程安全的Set,底层使用CopyOnWrteArrayList实现。
- 唯一不同在于,使用addIfAbsent()添加元素,会遍历数组,有序
- 如存在元素,则不添加(扔掉副本)
// 创建 CopyOnWriteArraySet
CopyOnWriteArraySet<String> list = new CopyOnWriteArraySet<>();
// 添加元素
list.add("苹果");
list.add("香蕉");
list.add("葡萄");
// 不可以添加重复的
list.add("苹果");
System.out.println(list.toString());
// 创建线程池
ExecutorService es = Executors.newFixedThreadPool(5);
// 提交线程任务
for(int i=0;i<5;i++){
es.submit(new Runnable() {
@Override
public void run() {
for(int j=0;j<10;j++){
list.add(Thread.currentThread().getName() +"..." + new Random().nextInt(1000));
}
}
});
}
// 关闭线程池
es.shutdown();
while(!es.isTerminated()){}
// 打印结果
for (String s : list) {
System.out.println(s);
}
- Collection的子接口,表示队列FIFO(First In First Out)先进先出
- 常用方法:
- 抛出异常:
- boolean add(E e) 顺序添加一个元素(到达上限后,再添加则会报异常)
- E remove() 获得第一个元素并移除(如果队列没有元素则抛异常)
- E element() 获得第一个元素但不移除 (如果队列没有元素,则抛异常)
- 返回特殊值: 推荐使用
- boolean offer(E e) 顺序添加一个元素(到达上限后,再添加则会返回false)
- E poll() 获得第一个元素并移除 (如果队列没有元素时,则返回null)
- E peek() 获得第一个元素但不移除(如果队列没有元素时,则返回null)
// 创建 Queue 队列
Queue<String> queue = new LinkedList<>();
// 入队 offer() 顺序添加元素
queue.offer("苹果");
queue.offer("香蕉");
queue.offer("葡萄");
int size = queue.size();
// 获取第一个元素 但不移除 peek()
System.out.println(queue.peek());
System.out.println("---------");
// 出列 poll()
for(int i=0;i<size;i++){
System.out.println("出列:" + queue.poll());
}
- 线程安全丶可高效读写的队列丶高并发下性能最好的队列。
- 无锁丶CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)
- V:要更新的变量丶E:预期值丶N:新值
- 只有当 V == E 时,V = N ; 否则表示已被更新过,则取消当前操作
// 创建 ConcurrentLinkedQueue()
ConcurrentLinkedQueue concurrentLinkedQueue = new ConcurrentLinkedQueue();
// 线程操作ConcurrentLinkedQueue() 入队
// 创建线程
Thread th1 = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<=5;i++){
concurrentLinkedQueue.offer(i);
}
}
});
Thread th2 = new Thread(new Runnable() {
@Override
public void run() {
for(int i=6;i<=10;i++){
concurrentLinkedQueue.offer(i);
}
}
});
th1.start();
th2.start();
th1.join();
th2.join();
// 出列 poll()
int size = concurrentLinkedQueue.size();
for(int i=0;i<size;i++){
System.out.println("出列:" + concurrentLinkedQueue.poll());
}
- Queue的子接口丶阻塞的队列,增加了两个线程状态为无限期等待的方法
- 方法:
- void put(E e) 将指定元素插入此队列中,如果没有可用空间,则等待
- E take() 获取并移除此队列头部元素,如果没有可用元素,则等待。
- ArrayBlockingQueue:
- 数组结构实现,有界队列。(手工固定上限)
- LinkedBlockingQueue:
- 链表结构实现,有界队列。(默认上限Integer.MAX_VALUE)
// 创建 ArrayBlockingQueue(capacity) 阻塞队列
ArrayBlockingQueue<String> aq = new ArrayBlockingQueue(3);
// 添加 put() 超出容量阻塞
aq.put("葡萄");
aq.put("香蕉");
aq.put("橘子");
// 删除元素
aq.take();
System.out.println("已经添加了3个元素了");
// 超出容量阻塞
aq.put("苹果");
System.out.println("已经添加了4个元素了");
System.out.println(aq.toString());
// 创建 LinkedBlockingQueue(capacity) 阻塞队列
LinkedBlockingQueue<String> lq = new LinkedBlockingQueue(3);
// 添加 put() 超出容量阻塞
lq.put("葡萄");
lq.put("香蕉");
lq.put("橘子");
// 删除元素
lq.take();
System.out.println("已经添加了3个元素了");
// 超出容量阻塞
lq.put("苹果");
System.out.println("已经添加了4个元素了");
System.out.println(lq.toString());
public static void main(String[] args) {
// 创建阻塞队列 ArrayBlockingQueue(capacity)
ArrayBlockingQueue<Integer> aq = new ArrayBlockingQueue<>(6);
// 创建线程
Thread th1 = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<30;i++){
try {
aq.put(i);
System.out.println(Thread.currentThread().getName() + "添加了" + i + "号元素" );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"熊大");
Thread th2 = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<30;i++){
try {
Integer num = aq.take();
System.out.println(Thread.currentThread().getName() + "移除了" + i + "号元素" );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"熊二");
th1.start();
th2.start();
}
- 初始容量默认为16段(Segment),使用分段锁设计。线程安全的。
- 不对整个Map加锁,而是为每个Segment加锁。
- 当多个对象存入同一个Segment时,才需要互斥。
- 最理想状态为16个对象分别存入16个Segment,并行数量16
- 使用方式与HashMap无异。
- JDK8之后ConcurrentHashMap采用CAS无锁,效率比分段锁更高。
// 创建 ConcurrentHashMap 线程安全的
ConcurrentHashMap<String,String> chm = new ConcurrentHashMap<>();
// 多线程操作添加
for(int i=0;i<5;i++){
new Thread(new Runnable() {
@Override
public void run() {
for(int j=0;j<20;j++){
chm.put(Thread.currentThread().getName() + "--" + j ,j+"");
System.out.println(chm);
}
}
}).start();
}