- 1.java中有几个线程
- 2.wait和sleep的区别
- 3.锁
- 1.线程多线程异常
- 2.使用synchronized解决线程安全异常
- 3.使用lok锁解决线程安全
- 4.Lok和Synchronized的区别
- 4.生产者和消费者
- 1.复习基础篇
- 2.虚假唤醒
- 3.JUC版
- 4.线程精确唤醒
- 总结
- 5.多线程8锁解析
- 1.标准访问,先打印短信还是邮件
- 2.在邮件方法内听四秒,先打印短信还是邮件?
- 3.普通的hello方法,事先打印邮件还是hello
- 4.两部手机,先打印邮件还是hello
- 5.两个静态同步方法,一部手机,先打印短信还是邮件
- 6.两个静态同步方法,两部手机,先打印短信还是邮件
- 7.一个静态同步方法,一个普通同步方法, 一部手机,先打印短信还是先打印邮件
- 8.一个静态同步方法,一个普通同步方法, 两部手机,先打印短信还是先打印邮件
- 6.set list map并发安全
- 1.list
- 2.set
- 3.Map
- 7.线程减数器
- 1.CountDownLatch
- 2.CyclicBarrier
- 3.semaphore
- 8.读写锁
- 9.Callable
- 1.Callable和runnble接口的区别
- 10.阻塞队列
- 11.线程池
- 线程池的七大参数
- 线程池原理
- 拒绝策列
- 自定义线程池
2.wait和sleep的区别两个
1.main方法
2.GC线程
3.锁 1.线程多线程异常1.来自不同的类 : wait=>object sleep=>Thread
2.关于锁得释放 : wait->会释放锁 sleep->不会释放锁
3.使用范围不同 :wait必须在同步代码块中执行 sleep可以在任何地方睡
4.是否需要捕获异常 : wait 也需要捕获异常 sleep 必须要捕获异常
代码如下:
package com.xjggb.test01;
public class Ticket {
public static void main(String[] args) {
Ticket01 ticket01 = new Ticket01();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 40; i++) {
ticket01.saleTicket();
}
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 40; i++) {
ticket01.saleTicket();
}
}
},"B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 40; i++) {
ticket01.saleTicket();
}
}
},"C").start();
}
}
//创建资源类
class Ticket01{
//创建30张票
private int number=100;
public void saleTicket(){
try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (number>0){
System.out.println(Thread.currentThread().getName()+"t卖出第:"+(number--)+"t还剩下:"+number);
}
}
}
结果显示
小结:
- 多线程操作一个共享资源的话就会出现线程安全问题
- 如何解决线程安全问题 加锁
package com.xjggb.test01;
public class Demo03 {
public static void main(String[] args) {
//创建资源类
Ticket02 ticket02 = new Ticket02();
//lamb表达式创建线程
new Thread(()->{ for (int i = 0; i <40 ; i++) ticket02.show();},"A").start();
new Thread(()->{ for (int i = 0; i <40 ; i++) ticket02.show();},"B").start();
new Thread(()->{ for (int i = 0; i <40 ; i++) ticket02.show();},"C").start();
}
}
class Ticket02 {
//创建30张票
private int number = 100;
//给方法加锁
public synchronized void show() {
try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "t卖出第:" + (number--) + "t还剩下:" + number);
}
}
}
结果
3.使用lok锁解决线程安全package com.xjggb.test01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo04 {
public static void main(String[] args) {
//创建资源类
Ticket03 ticket03 = new Ticket03();
new Thread(() -> {
for (int i = 0; i < 40; i++) ticket03.show();
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) ticket03.show();
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) ticket03.show();
}, "C").start();
}
}
class Ticket03 {
//创建30张票
private int number = 100;
//创建lok锁
private Lock lock = new ReentrantLock();
//给方法加锁
public void show() {
lock.lock(); //加锁
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "t卖出第:" + (number--) + "t还剩下:" + number);
}
} finally {
lock.unlock(); //释放锁
}
}
}
结果:
4.Lok和Synchronized的区别- Synchronized 是java关键字,lock是一个类
- Synchronized无法获取锁的状态,Lock可以判断是否得到锁
- Synchronized可以自动释放锁 Lock需要手动释放锁,如果不释放就会死锁
- Synchronized 可重入锁(就是可以重复获取相同的锁,),不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以自己设置);
- Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
package com.xjggb.test02;
public class Test01 {
public static void main(String[] args) {
Product product = new Product();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
product.increment();
}
},"A").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
product.decrement();
}
},"B").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
product.increment();
}
},"C").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
product.decrement();
}
},"D").start();
}
}
class Product {
private int product = 0;
//生产产品
public synchronized void increment() {
if (product != 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
product++;
this.notifyAll();
System.out.println(Thread.currentThread().getName() + product);
}
//消费产品
public synchronized void decrement() {
if (product == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
product--;
this.notifyAll();
System.out.println(Thread.currentThread().getName() + product);
}
}
小结:
- 多线程下上面的代码会出错,就是用if判断的话唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
这也就是为什么用while而不用if的原因了,因为线程被唤醒后,执行开始的地方是wait之后
-
多线程交互中,必须防止多线程的虚假唤醒
-
虚假唤醒的原因:
可能多个线程进入if后被wait,而if只判断一次,然后因为某个线程的notifyAll后仔执行下去,导致连续多次++或–
package com.xjggb.test02;
public class Test01 {
public static void main(String[] args) {
Product product = new Product();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
product.increment();
}
},"A").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
product.decrement();
}
},"B").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
product.increment();
}
},"C").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
product.decrement();
}
},"D").start();
}
}
class Product {
private int product = 0;
//生产产品
public synchronized void increment() {
while (product != 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
product++;
this.notifyAll();
System.out.println(Thread.currentThread().getName() + product);
}
//消费产品
public synchronized void decrement() {
while (product == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
product--;
this.notifyAll();
System.out.println(Thread.currentThread().getName() + product);
}
}
3.JUC版
通过Lock找到 Condition 的await() signal
package com.xjggb.test02;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test02 {
public static void main(String[] args) {
//创建资源对象
Product02 product02 = new Product02();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
product02.show1();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
product02.show2();
}
}, "B").start();
}
}
class Product02 {
private int product = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void show1() {
lock.lock(); //添加锁
try {
while (product != 0) {
condition.await();
}
product++;
condition.signalAll();
System.out.println(Thread.currentThread().getName() + product);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); //释放锁
}
}
public void show2() {
lock.lock(); //添加锁
try {
while (product == 0) {
condition.await();
}
product--;
condition.signalAll();
System.out.println(Thread.currentThread().getName() + product);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); //释放锁
}
}
}
4.线程精确唤醒
package com.xjggb.test02;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test03 {
public static void main(String[] args) {
Print01 print01 = new Print01();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
print01.showA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
print01.showB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
print01.showC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
}
}
class Print01 {
private int amount = 1;
private Lock lock = new ReentrantLock();
private Condition A = lock.newCondition();
private Condition B = lock.newCondition();
private Condition C = lock.newCondition();
public void showA() throws InterruptedException {
lock.lock(); //添加锁
try {
while (amount != 1) {
A.await();
}
System.out.println(Thread.currentThread().getName() + "=>AAAAAAA");
// 唤醒,唤醒指定的人,B
amount = 2;
B.signal(); //唤醒
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void showB() throws InterruptedException {
lock.lock(); //添加锁
try {
while (amount != 2) {
B.await();
}
System.out.println(Thread.currentThread().getName() + "=>BBB");
// 唤醒,唤醒指定的人,B
amount = 3;
C.signal(); //唤醒
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void showC() throws InterruptedException {
lock.lock(); //添加锁
try {
while (amount != 3) {
C.await();
}
System.out.println(Thread.currentThread().getName() + "=>CCC");
// 唤醒,唤醒指定的人,B
amount = 1;
A.signal(); //唤醒
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
总结
- Synchronized不能唤醒某个线程
- lock支持多个线程唤醒
- 可以让某个锁线程放开锁
package com.xjggb.test08;
import java.util.concurrent.TimeUnit;
public class Phone {
public synchronized void sendEmail() throws InterruptedException {
System.out.println("======== sendEmail");
}
public synchronized void sendSMS() {
System.out.println("======== sendSMS");
}
public void hello() {
System.out.println("===hello");
}
}
class Demo01 {
public static void main(String[] args) {
//创建资源类
Phone phone = new Phone();
//创建线程A
new Thread(() -> {
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//创建线程B
new Thread(() -> {
phone.sendSMS();
}, "B").start();
}
}
2.在邮件方法内听四秒,先打印短信还是邮件?======== sendEmail
======== sendSMS由答案可以看出邮件先执行
package com.xjggb.test08;
import java.util.concurrent.TimeUnit;
public class Phone {
public synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(4); //暂停两秒
System.out.println("======== sendEmail");
}
public synchronized void sendSMS() {
System.out.println("======== sendSMS");
}
public void hello() {
System.out.println("===hello");
}
}
class Demo01 {
public static void main(String[] args) {
//创建资源类
Phone phone = new Phone();
//创建线程A
new Thread(() -> {
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//创建线程B
new Thread(() -> {
phone.sendSMS();
}, "B").start();
}
}
======== sendEmail
======== sendSMS由答案可以看出邮件先执行
小结:
3.普通的hello方法,事先打印邮件还是hello一个对象里面如果有多个synchronized方法,某一时刻内只要一个线程去调用其中的一个synchronized方法,其他线程都只能等待,换句话说,某一时刻内,只能唯一一个线程去访问这些synchronized方法,锁的是当前对象this,被锁定后,其他的线程都不能进入当前对象的其他synchronized方法
package com.xjggb.test08;
import java.util.concurrent.TimeUnit;
public class Phone {
public synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(4); //暂停两秒
System.out.println("======== sendEmail");
}
public synchronized void sendSMS() {
System.out.println("======== sendSMS");
}
public void hello() {
System.out.println("===hello");
}
}
class Demo01 {
public static void main(String[] args) {
//创建资源类
Phone phone = new Phone();
//创建线程A
new Thread(() -> {
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//创建线程B
new Thread(() -> {
phone.hello();
}, "B").start();
}
}
4.两部手机,先打印邮件还是hello运行结果
===hello
======== sendEmail
package com.xjggb.test08;
import java.util.concurrent.TimeUnit;
public class Phone {
public synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(4); //暂停两秒
System.out.println("======== sendEmail");
}
public synchronized void sendSMS() {
System.out.println("======== sendSMS");
}
public void hello() {
System.out.println("===hello");
}
}
class Demo01 {
public static void main(String[] args) {
//创建资源类
Phone phone = new Phone();
Phone phone2 = new Phone();
//创建线程A
new Thread(() -> {
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//创建线程B
new Thread(() -> {
phone2.hello();
}, "B").start();
}
}
===hello
======== sendEmail
小结:
5.两个静态同步方法,一部手机,先打印短信还是邮件加一个普通方法后发现和同步锁无关,换成两个对象后,不是同一把锁了,情况立刻变化
package com.xjggb.test08;
import java.util.concurrent.TimeUnit;
public class Phone {
public static synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(4); //暂停两秒
System.out.println("======== sendEmail");
}
public static synchronized void sendSMS() {
System.out.println("======== sendSMS");
}
public void hello() {
System.out.println("===hello");
}
}
class Demo01 {
public static void main(String[] args) {
//创建资源类
Phone phone = new Phone();
// Phone phone2 = new Phone();
//创建线程A
new Thread(() -> {
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//创建线程B
new Thread(() -> {
//phone2.hello();
phone.sendSMS();
}, "B").start();
}
}
6.两个静态同步方法,两部手机,先打印短信还是邮件结果如下
======== sendEmail
======== sendSMS
package com.xjggb.test08;
import java.util.concurrent.TimeUnit;
public class Phone {
public static synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(4); //暂停两秒
System.out.println("======== sendEmail");
}
public static synchronized void sendSMS() {
System.out.println("======== sendSMS");
}
public void hello() {
System.out.println("===hello");
}
}
class Demo01 {
public static void main(String[] args) {
//创建资源类
Phone phone = new Phone();
Phone phone2 = new Phone();
//创建线程A
new Thread(() -> {
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//创建线程B
new Thread(() -> {
//phone2.hello();
phone2.sendSMS();
}, "B").start();
}
}
======== sendEmail
======== sendSMS
小结:
7.一个静态同步方法,一个普通同步方法, 一部手机,先打印短信还是先打印邮件可以看出5和6与Phone 无关静态同步方法锁的是 Phone.class,唯一模板
package com.xjggb.test08;
import java.util.concurrent.TimeUnit;
public class Phone {
public static synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(4); //暂停两秒
System.out.println("======== sendEmail");
}
public synchronized void sendSMS() {
System.out.println("======== sendSMS");
}
public void hello() {
System.out.println("===hello");
}
}
class Demo01 {
public static void main(String[] args) {
//创建资源类
Phone phone = new Phone();
Phone phone2 = new Phone();
//创建线程A
new Thread(() -> {
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//创建线程B
new Thread(() -> {
//phone2.hello();
phone.sendSMS();
}, "B").start();
}
}
8.一个静态同步方法,一个普通同步方法, 两部手机,先打印短信还是先打印邮件
package com.xjggb.test08;
import java.util.concurrent.TimeUnit;
public class Phone {
public static synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(4); //暂停两秒
System.out.println("======== sendEmail");
}
public synchronized void sendSMS() {
System.out.println("======== sendSMS");
}
public void hello() {
System.out.println("===hello");
}
}
class Demo01 {
public static void main(String[] args) {
//创建资源类
Phone phone = new Phone();
Phone phone2 = new Phone();
//创建线程A
new Thread(() -> {
try {
phone.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
//创建线程B
new Thread(() -> {
// phone2.hello();
phone2.sendSMS();
}, "B").start();
}
}
======== sendSMS
======== sendEmail
小结总结
由运行结果可以看出,数据跟5和6一样,静态同步方法锁的是Phone.class,普通同步方法锁的是当前对象
synchronized实现同步的基础:java中的每一个对象都可以作为锁如下三种方式:
6.set list map并发安全 1.list
- 当以一个线程视图访问同步代码块时,他先获得锁,退出或抛出异常时释放锁
- 如果一个实例对象的并非静态同步方法获得锁后,该实例对象的奇特非静态同步方法必须等待获取锁的方法释放锁后才能获得锁。
- 所有的静态同步方法用的也是同一锁,就是类对象模板,所以静态同步方法与非静态同步方法之间是不会竞争条件的,但是一个静态方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,不管是不是多个实例对象中的静态同步方法。
package com.xjggb.test03;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class Lest01 {
public static void main(String[] args) {
//初始版本
// list01();
//第一种方式
// list02();
//第二种方式
list04();
}
public static void list01(){
ArrayList list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println("list = " + list);
},String.valueOf(i)).start();
// java.util.ConcurrentModificationException 报错
}
}
//解决方式一
public static void list02(){
Vector list = new Vector<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println("list = " + list);
},String.valueOf(i)).start();
// java.util.ConcurrentModificationException 报错
}
}
//
//解决方式二
public static void list03(){
List list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println("list = " + list);
},String.valueOf(i)).start();
}
}
//解决方式二
public static void list04(){
CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println("list = " + list);
},String.valueOf(i)).start();
}
}
}
2.set
package com.xjggb.test03;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
public class Lest02 {
public static void main(String[] args) {
// 线程不安全
// set01();
set03();
}
public static void set01(){
HashSet set = new HashSet<>();
for (int i = 0; i <20 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println("set = " + set);
},String.valueOf(i)).start();
}
}
public static void set02(){
Set set = Collections.synchronizedSet(new HashSet());
for (int i = 0; i <20 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println("set = " + set);
},String.valueOf(i)).start();
}
}
public static void set03(){
CopyOnWriteArraySet set = new CopyOnWriteArraySet<>();
for (int i = 0; i <20 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println("set = " + set);
},String.valueOf(i)).start();
}
}
}
3.Map
package com.xjggb.test03;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class Lest03 {
public static void main(String[] args) {
//初始版
// map01();
//线程安全版
map02();
}
public static void map01(){
HashMap map = new HashMap<>();
for (int i = 1; i <= 50; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println("map = " + map);
},String.valueOf(i)).start();
}
}
public static void map02(){
Map map = Collections.synchronizedMap(new HashMap());
for (int i = 1; i <= 50; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println("map = " + map);
},String.valueOf(i)).start();
}
}
public static void map03(){
ConcurrentHashMap map = new ConcurrentHashMap<>(new HashMap<>());
for (int i = 1; i <= 50; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println("map = " + map);
},String.valueOf(i)).start();
}
}
}
小结:高并发下集合的使用
- CopyOnWriteArrayList<>();
- CopyOnWriteArraySet<>();
- ConcurrentHashMap<>(new HashMap<>());
可以理解为线程减数器,等所有线程都出门了就把问关上
package com.xjggb.test04;
import java.util.concurrent.CountDownLatch;
public class Test04 {
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()+"出门了 ");
countDownLatch.countDown(); // 技术器减一
},String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println("关门");
}
}
小结:
比如六个同学 得等到六个同学全部出了教室门后才能关门 ,
2.CyclicBarrierpackage com.xjggb.test04;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Test05 {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, new Runnable() {
@Override
public void run() {
System.out.println("召唤神龙");
}
});
for (int i = 0; i < 7; i++) {
final int a = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "收集" + a + "星龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
3.semaphore
package com.xjggb.test04;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class Test06 {
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();
}
}).start();
}
}
}
8.读写锁
package com.xjggb.test05;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Test01 {
public static void main(String[] args) {
MyCacheLock myCacheLock = new MyCacheLock();
//写入
for (int i = 0; i <5 ; i++) {
final int num= i;
new Thread(()->{
myCacheLock.put(num+"",num+"");
},String.valueOf(i)).start();
}
//读取
for (int i = 0; i <5 ; i++) {
final int temp=i;
new Thread(()->{
myCacheLock.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCacheLock {
private volatile Map map = new HashMap<>();
//读写锁
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//存数据的时候只希望一个线程写
public void put(String key, Object value) {
readWriteLock.writeLock().lock(); //获取写锁
try {
System.out.println(Thread.currentThread().getName() + "写入+" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入ok");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock(); //释放锁
}
}
//读,所有人都可以读
public void get(String key) {
readWriteLock.readLock().lock(); //获取读锁
try {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = map.get(key);
System.out.println("o = " + o);
System.out.println(Thread.currentThread().getName() + "读取" + key);
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock(); //释放读锁
}
}
}
小结:
读写锁,写入的时候一个一个线程的写,读取的时候可以多个线程去读
9.Callable 1.Callable和runnble接口的区别- 是否有返回值
- 是否抛异常
- 落地方法不一样,一个run一个是call
代码如下:
package com.xjggb.test05;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test06 {
public static void main(String[] args) {
// 一个futureTask不管多少个线程调都只会被调用一次
FutureTask integerFutureTask = new FutureTask<>(new MyThread2());
try {
new Thread(integerFutureTask,"A").start();
new Thread(integerFutureTask,"B").start();
Integer integer = integerFutureTask.get(); //尽量在后面取,否者导致程序堵塞
System.out.println("integer = " + integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyThread2 implements Callable {
@Override
public Integer call() {
System.out.println("来了老弟"+ Thread.currentThread().getName());
return 1024;
}
}
线程都是由上往下执行,比如下单功能和短信发送功能,假设下单功能需要五秒,短信发送需要五秒,加起来就十秒,当开启另一个线程一起执行两个线程并行,假设订单需要六秒,短信需要五秒那么最长时间就是六秒。因为是并行按最长的时间,
小结:
get()要放到最后一行
一个futureTask不管多少个线程调都只会被调用一次
10.阻塞队列当队列是空的,从队列中获取元素的操作将会被阻塞
当队列是满的, 从队列中添加元素的操作将会被阻塞。
试图从空的队列中获取元素的线程将会被阻塞, 直到其他线程往空的队列出入新的元素
试图向满的队列中添加新元素的线程将会被阻塞, 直到其他线程从队列中移除一个或者多个元素或者完全被清空, 使队列变得空闲起来并后续新增。
在多线程领域: 所谓阻塞, 在某些情况下会被挂起线程, 一旦条件满足, 挂起的线程又会自动被唤醒
为什么需要blockingQueue
好处是我们不需要关心什么时候需要阻塞线程, 什么时候需要唤醒线程, 因为这一切BlockingQueue都给你一手包办了
在concurent包发布以前, 在多线程环境下, 我们每个程序员都必须取自己控制这些细节, 尤其还有兼顾效率和线程安全, 而这会给我们的程序带来不小的复杂度。
package com.xjggb.test06;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class Test01 {
public static void main(String[] args) throws InterruptedException {
show4();
}
//抛出异常
public static void show1() {
ArrayBlockingQueue strings = new ArrayBlockingQueue<>(3);
//添加队列
System.out.println("strings.add = " + strings.add("A"));
System.out.println("strings.add = " + strings.add("B"));
System.out.println("strings.add = " + strings.add("C"));
// System.out.println("strings.add = " + strings.add("D")); 长度超过三就会就会报错 java.lang.IllegalStateException: Queue full
//获取队首
System.out.println("获取队首" + strings.element());
//取出队列
System.out.println("strings.remove() = " + strings.remove());
System.out.println("strings.remove() = " + strings.remove());
System.out.println("strings.remove() = " + strings.remove());
// System.out.println("strings.remove() = " + strings.remove()); 超过三个就会报错"main" java.util.NoSuchElementException
}
//特殊值
public static void show2() {
ArrayBlockingQueue strings = new ArrayBlockingQueue<>(3);
System.out.println("strings.offer+" + strings.offer("A")); //队不满返回true
System.out.println("strings.offer+" + strings.offer("B"));
System.out.println("strings.offer+" + strings.offer("C"));
System.out.println("strings.offer+" + strings.offer("A")); //队满返回false
System.out.println("strings.poll() = " + strings.poll());
System.out.println("strings.poll() = " + strings.poll());
System.out.println("strings.poll() = " + strings.poll());
System.out.println("strings.poll() = " + strings.poll()); //队空,返回空
}
public static void show3() throws InterruptedException {
ArrayBlockingQueue strings = new ArrayBlockingQueue<>(3);
strings.put("A");
strings.put("B");
strings.put("C"); //没有返回值
// 队满进入阻塞状态
// strings.put("D");
System.out.println("strings.take() = " + strings.take());
System.out.println("strings.take() = " + strings.take());
System.out.println("strings.take() = " + strings.take());
System.out.println("strings.take() = " + strings.take()); //队列空进入阻塞状态
}
public static void show4() throws InterruptedException {
ArrayBlockingQueue strings = new ArrayBlockingQueue<>(3);
System.out.println("strings.offer " + strings.offer("A"));
System.out.println("strings.offer " + strings.offer("B"));
System.out.println("strings.offer " + strings.offer("C"));
// System.out.println("TimeUnit.SECONDS = " + TimeUnit.SECONDS);
// System.out.println("strings.offer " + strings.offer("D",3L, TimeUnit.SECONDS)); 队满阻塞
strings.poll();
strings.poll();
strings.poll();
strings.poll(3L, TimeUnit.SECONDS); //队满阻塞一定的时间还不能为空就走
}
}
11.线程池
线程池的优势
线程池做的工作只要控制运行的线程数量,处理过程中将任务放入队列,然后线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,在从队列中取出任务来执行
主要特点 : 线程复用 控制最大并发数,管理线程
- 降低资源消耗,通过重复利用自己创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度,当任务到达时,任务可以不需要等待线程创建能立即执行
- 提高线程可管理性:线程是稀缺资源,如果无线创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池统一分配,调优和监控
package com.xjggb.test07;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorsTest {
public static void main(String[] args) {
show3();
}
//单个线程池
public static void show1(){
ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i <10 ; i++) {
threadExecutor.execute(()->{
System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
});
}
}
//定义五个线程
public static void show2(){
ExecutorService threadExecutor = Executors.newFixedThreadPool(5);
for (int i = 0; i <10 ; i++) {
threadExecutor.execute(()->{
System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
});
}
}
//定义定义弹性扩容
public static void show3(){
ExecutorService threadExecutor = Executors.newCachedThreadPool();
for (int i = 0; i <10 ; i++) {
threadExecutor.execute(()->{
System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
});
}
threadExecutor.shutdown();
}
}
线程池的七大参数
线程池原理
拒绝策列
abortPolicy(默认):直接抛出 RejectedExecutionException
CallerRunPolicy: "调用者运行“ 一种调节机制,该策略即不会抛弃任务, 也不会抛出异常, 而是将某些任务回退到调用者, 从而降低新任务的流量(谁让你来找到线程池的就退回给谁)
discardOldestPolicy: 抛弃队列中等待最久的任务, 然后把当前任务加入队列中尝试在其提交任务
discardPolicy: 该策略默默地丢弃无法处理的任务, 不予任何处理也不报异常, 如果允许任务丢失, 这是最好的一种策略
package com.xjggb.text;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test01 {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2, //核心线程池大小
5, //最大核心线程池大小
2, //超时没有调用就会释放
TimeUnit.SECONDS, //超时单位
new LinkedBlockingDeque<>(3), //线程工厂,一般不修改
new ThreadPoolExecutor.DiscardOldestPolicy() //一种拒绝策略(队列满了,尝试和最早的去竞争,也不会抛出异常)
);
try {
for (int i = 0; i <10 ; i++) {
threadPoolExecutor.execute(()->{
System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown(); //关闭线程池
}
}
}



