1:继承thread类,重写run方法
2:实现runnable接口
3:实现callable接口
第一种方式:继承thread类
package com.wangjunji.qifeng;
//继承thread类
public class TestCreateThread extends Thread {
public TestCreateThread(String name) {
super(name);
}
//覆盖run方法
@Override
public void run() {
for (int i = 0; i < 50; i++) {
//如果继承thread的类s可以使用this.getId 或 this.getName
System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId()+":"+i);
}
}
public static void main(String[] args) throws InterruptedException {
//创建对象
TestCreateThread myThreadName = new TestCreateThread("myThreadName");
TestCreateThread myThreadName1 = new TestCreateThread("myThreadName1");
//调用run方法
myThreadName.setName("我的子线程1");
myThreadName1.setName("我的子线程2");
myThreadName.start();
myThreadName1.start();
myThreadName.join();
myThreadName1.join();
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId()+":"+i);
}
}
}
获取和修改线程的名称
获取线程ID和线程名称
1、在thread的子类中调用this.getid() 或this.getName()
2、使用thread.currentThread().getId()和thread.currentThread.getName()
修改线程名称
1、调用线程对象的setName方法
2、使用线程子类的构造方法赋值 看如上方法
案例:使用继承thread类实现4个窗口卖100张票
package com.wangjunji.qifeng.second;
public class TiceWIn extends Thread{
private int count =100;
@Override
public void run() {
int num = count;
for (int i = 0; i < num; i++) {
count = count -1;
System.out.println("Thread name:"+this.getName()+":Thread id is :"+this.getId()+":"+count);
}
}
}
package com.wangjunji.qifeng.second;
import com.wangjunji.qifeng.Ticks;
public class Sellor {
public static void main(String[] args) {
TiceWIn t1 = new TiceWIn();
TiceWIn t2 = new TiceWIn();
TiceWIn t3 = new TiceWIn();
TiceWIn t4 = new TiceWIn();
t1.setName("一号窗口");
t2.setName("二号窗口");
t3.setName("三号窗口");
t4.setName("四号窗口");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
四个人卖1000张票
package com.wangjunji.qifeng;
public class SellTickDoor {
public static void main(String[] args) throws InterruptedException {
Ticks ticks = new Ticks();
SellTicks t1 = new SellTicks(ticks);
SellTicks t2 = new SellTicks(ticks);
SellTicks t3 = new SellTicks(ticks);
SellTicks t4 = new SellTicks(ticks);
t1.setName("第一名");
t2.setName("第二名");
t3.setName("第三名");
t4.setName("第四名");
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
}
}
package com.wangjunji.qifeng;
public class SellTicks extends Thread {
private Ticks ticks;
Object obj = new Object();
public SellTicks(Ticks ticks) {
this.ticks = ticks;
}
@Override
public void run() {
while (true) {
if(ticks.getCount()>0) {
synchronized (obj) {
ticks.sellcount();
System.out.println(this.getName() + "卖了一张" + "余票还有" + ticks.getCount() + "张");
}
}else{
break;
}
}
}
}
package com.wangjunji.qifeng;
public class Ticks {
private int count =1000;
public int getCount() {
return count;
}
public void sellcount(){
count = count -1 ;
}
}
package com.wangjunji.qifeng;
public class MyRunable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("名字"+Thread.currentThread().getName()+"Threadid"+Thread.currentThread().getId()+"i"+i);
}
}
}
package com.wangjunji.qifeng;
public class Test {
public static void main(String[] args) {
/* MyRunable myRunable = new MyRunable();
new Thread(myRunable).start();*/
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("名字" + Thread.currentThread().getName() + "Threadid" + Thread.currentThread().getId() + "i" + i);
}
}
};
new Thread(runnable).start();
for (int i = 0; i < 100; i++) {
System.out.println("名字"+Thread.currentThread().getName()+"Threadid"+Thread.currentThread().getId()+"i"+i);
}
}
}
你和你女朋友共用一个银行卡,你向卡中存钱,你女朋友从卡中取钱,使用程序模拟过程
package com.wangjunji.qifeng.card;
public class BankCard {
private double money;
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
package com.wangjunji.qifeng.card;
public class AddMony implements Runnable{
BankCard bankCard;
public AddMony(BankCard bankCard) {
this.bankCard = bankCard;
}
@Override
public void run() {
for (int i = 0; i < 30; i++) {
double money = bankCard.getMoney();
money = money+1000;
bankCard.setMoney(money);
System.out.println(Thread.currentThread().getName()+"存入一千元"+bankCard.getMoney());
}
}
}
package com.wangjunji.qifeng.card;
public class SubMony implements Runnable{
BankCard bankCard;
public SubMony(BankCard bankCard) {
this.bankCard = bankCard;
}
@Override
public void run() {
for (int i = 0; i < 30; i++) {
double money = bankCard.getMoney();
if(money<0){
System.out.println("余额不足");
}else{
money = money-1000;
bankCard.setMoney(money);
System.out.println(Thread.currentThread().getName()+"取出一千元"+bankCard.getMoney());
}
}
}
}
package com.wangjunji.qifeng.card;
public class TestBankCard {
public static void main(String[] args) {
BankCard bankCard = new BankCard();
AddMony ad = new AddMony(bankCard);
SubMony sd = new SubMony(bankCard);
Thread chen = new Thread(ad,"晨晨");
Thread bing = new Thread(sd,"冰冰");
chen.start();
bing.start();
}
}
线程对象被创建,即为初始状态 new 初始状态 调用start后进入ready就绪状态 os选中/时间片到期 running运行状态
调用start()之后,进入就绪状态,等待os选中,并分配时间片。
常见方法
休眠 public static void sleep
设置线程优先级
优先级:
线程对象.setPriority()
线程优先级1-10,默认为5 ,优先级越高,表示获取cpu机会越多
守护线程:
线程对象.setDaemon(true);设置守护线程
线程有两类:用户线程(前强线程),守护线程(后台线程)
如果程序中所有前台线程都执行完毕,后台线程会自动结束
垃圾回收器线程属于守护线程
package com.wangjunji.qifeng.card;
public class PriorityThread extends Thread {
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String[] args) {
PriorityThread priorityThread = new PriorityThread();
PriorityThread priorityThread1 = new PriorityThread();
priorityThread.setName("thread-1");
priorityThread1.setName("thread-2");
priorityThread.setPriority(10);
priorityThread1.setPriority(1);
priorityThread.start();
priorityThread1.start();
}
}
线程安全问题:
A线程---查找下标0,时间片到期 --->将hello存入下标为0的位置
B线程---查找下标0,时间片到期--->将world存入下标的位置
需求:A线程将hello存入数组
B线程将world存入数组
多线程安全问题:
当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
临界资源:共享资源(同一对象),一次仅允许一个线络使用,才可能保证其正确性。
原子操作,不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省。
package com.wangjunji.qifeng;
public class ThreadSafe {
private static int index=0;
public static void main(String[] args) throws InterruptedException {
String[] str = new String[5];
Runnable runnable1 = new Runnable() {
@Override
public void run() {
str[index] = "hello";
index++;
}
};
Runnable runnable = new Runnable() {
@Override
public void run() {
str[index] = "world";
index++;
}
};
Thread thread = new Thread(runnable);
Thread thread1 = new Thread(runnable1);
Thread thread3= new Thread(runnable);
Thread thread4 = new Thread(runnable1);
thread.start();
thread1.start();
thread3.start();
thread4.start();
thread.join();
thread1.join();
thread3.join();
thread4.join();
for (String s : str) {
System.out.println(s);
}
}
}
思考:在程序中应用中,如何保证线程的安全性。
同步方式(1):
同步代码块:synchronized(临界资源对象) //对临界资源对象加锁
//代码(原子操作)
package com.wangjunji.qifeng;
public class ThreadSafe {
private static int index=0;
public static void main(String[] args) throws InterruptedException {
String[] str = new String[5];
Runnable runnable1 = new Runnable() {
@Override
public void run() {
synchronized (str){
str[index] = "hello";
index++;}
}
};
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (str){
str[index] = "world";
index++;}
}
};
Thread thread = new Thread(runnable);
Thread thread1 = new Thread(runnable1);
Thread thread3= new Thread(runnable);
Thread thread4 = new Thread(runnable1);
thread.start();
thread1.start();
thread3.start();
thread4.start();
thread.join();
thread1.join();
thread3.join();
thread4.join();
for (String s : str) {
System.out.println(s);
}
}
}
每个对象都有一个互斥锁标记,用来分配给线程
只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。
synchronized返回值类 方法名称(形参列表0) //对当前对象this加锁 ,如果是静态方法则是 class
同步方法:
注:只有拥有对象的互斥标记的线程,才能进入该对象加锁的同步方法中。
线程退出同步方法时,会释放相应的互斥锁标记。
同步规则:
注意:只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。
如调用不包含不包含代码的方法,或普通方法时,则不需要锁标记,可直接调用。
已知jdk中线程安全的类
stringbuffer
vector
hashtable
以上类中的公开方法,均为synchnoized的同步方法。
死锁:
当第一个线程拥有a对象锁标记,并等待b标记,同时第二个线程拥有b对象锁标记,并等待A对象锁标记时,产生死锁。
一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,因此可能造成死锁。
//死锁代码
package com.wangjunji.qifeng.card;
public class Resrouce {
public static Object objA = new Object();
public static Object objB = new Object();
}
//
package com.wangjunji.qifeng.card;
public class BoyInfo extends Thread {
public void run(){
synchronized (Resrouce.objA){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("boygetInfo get a");
synchronized (Resrouce.objB){
System.out.println("boygetinfo get b ");
}
}
}
}
//
package com.wangjunji.qifeng.card;
public class grirlInfo extends Thread{
public void run(){
synchronized (Resrouce.objB){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("grirlInfo get b");
synchronized (Resrouce.objA){
System.out.println("grirlInfo get a ");
}
}
}
}
package com.wangjunji.qifeng.card;
public class UseInfo {
public static void main(String[] args) {
BoyInfo boyInfo = new BoyInfo();
grirlInfo grilinfo = new grirlInfo();
boyInfo.start();
grilinfo.start();
}
}
等待:等待队列
public final void wait()
public final void wait(long timeout )
必须在对obj加锁的同步代码块中,在一个线程中,调用obj.wait()时,此线程会释放其拥有所有锁标记,同时此线程阻塞在o的等待队列中,释放锁,进入等待队列。
通知:
public final void notify()
public final void notifyall()
经典问题:
生产者,消费者
若干个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使用生产者和消费者能并发执行,在两者之间一个能存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产向一个满的缓冲区中放入产品。
线程的创建:
继承thread
实现runnable接品,传入给thread对象执行。
线程安全:
同步方法块:为方法中的局部代码加锁
同步方法,为方法的所有代码加锁
高级多线程:
1)线程池概念
问题:
线程是宝贵的内存资源,单个线程约占1Mb空间,过多分配易造成内存溢出。
频繁的创建及销毁线程会增加虚拟机回收频率,资源开销,造成程序性能下降。
池程池:
线程容器,可设定线程分配的数量的上限。
将预先创建的线程对象存入池中,并重用线程池中的线程对象
避免频繁的创建和销毁。
线程池原理:
task1
task2 -----> threadpool
task3
将任务提交给线程池,由线程池分配线程,运行任务,并在当前任务结束后复用线程。
创建线程池:
常用的线程池接口和类(所有包java.util.concurrent里)
Executor 线程池的顶级接口
executorservice:线程线接口,可通过submit(runable task)提交任务代码。
executors工厂类,通过此类可以获得一个线程池。
通过newFixedThreadpool(int nthreads)获取固定数量的线程池,参数指线程池中线程的数量。
通过newCachedthreadpools获得动态数量的线程池,如不够则创建新的,没有上限。
创建线程池
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 演示线程池的创建
* Executor:线程池的根接口,execute()
* executorService:包含管理线程池的一些方法,submit shutdown
* threadpoolExecutor
* scheduledthreadpoolexecutor
* executor:创建线程池的工具类
* (1) 创建固定线程个数池程池
* (2) 创建缓存线程池,由任务的多少决定
* (3):创建单线程池
* (4)创建调度线路程池,调度,周期,定时执行。
*
*/
public class Demo01 {
public static void main(String[] args) {
//设置提交的个数
//ExecutorService executorService = Executors.newFixedThreadPool(4);
//不设置提交的个数,创缓存线程池,线程个数
ExecutorService executorService = Executors.newCachedThreadPool();
// 创建单线程Executors.newSingleThreadExecutor();
//创建调度线程池,调度,周期,定时执行 Executors.newScheduledThreadPool(5);
Runnable runnable = new Runnable() {
private int ticket = 10000;
public void run() {
while (true){
if(ticket<=0){
break;
}
ticket--;
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
}
}
};
//提交任务
for (int i = 0; i < 4; i++) {
executorService.submit(runnable);
}
//关闭线程,不然程序结束不了,等待所有任务执行完毕,才能结束。
executorService.shutdown();
}
}
Callable接口:
public interface Callable {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
jdk1.5加入,与runnable接口类似,实现之后代表一个线程任务
callable具有泛型返回值,可以声明异常。
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 演示callable接口的使用
* callable和runable接口的区别
* callable接口中call方法有返回值,runbable中run方法没有返回值
* callable接口中call方法有声明异常,runable接口中run方法没有异常
*/
public class Demo02 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//功能需求,使用callable实现1~100的和
//创建callable对象
Callable callable= new Callable() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"开始计算");
int sum=0;
for (int i = 1; i <= 100; i++) {
sum=sum+i;
}
return sum;
}
};
//2。不能交给线程。需要把callable对象转成任务
FutureTask task = new FutureTask<>(callable);
//创建线程
Thread thread = new Thread(task);
//启动线程
thread.start();
//获取 结果,等等call执行完毕,返回结果
Integer integer = task.get();
System.out.println(integer);
}
}
使用callable与线程池结合起来使用
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.*;
/**
* callable与线程池结合起来用
* 使用线程池来计算1-100的和
*/
public class Demo03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
//提交任务,表示将要执行完任务的结果
Future taskinfo = executorService.submit(new Callable() {
@Override
public Integer call() throws Exception {
System.out.println("开始执行作务");
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum = sum + i;
}
return sum;
}
});
//3获取结果
System.out.println(taskinfo.get());
executorService.shutdown();
}
}
Future接口 :
future表示将要完成任务的结果
需求,使用两个线程,并发计算1~50,51~100的和,再进行汇总计算。
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.*;
public class Demo04 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService task = Executors.newFixedThreadPool(2);
Future item1 = task.submit(new Callable() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 50; i++) {
sum = sum + i;
}
return sum;
}
});
Future item2 = task.submit(new Callable() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 50; i <= 100; i++) {
sum = sum + i;
}
return sum;
}
});
int allcount =item1.get()+item2.get();
System.out.println(allcount);
task.shutdown();
}
}
future接口
表示executorservice.sumbit所返回的状态结果,就是call的返回值
方法:v get()以阻塞形式等待,future中的异步处理结果(call的返回值)
思考什么是同步,什么是异步
同步:形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续
异步:形容一次调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回,二者竞争时间片,并发执行。
Lock接口:
jdk5加入,与synchronized比较,显示定义,结构更灵活。
提供更多实用方法,功能更强大,性能更优越。
常用方法:
void lock()://获取锁,如锁被占用,则等待
boolean tryLock()://尝试获取锁,成功返回true,失败返回false,不阻塞
void unlock释放锁。
重入锁:
拿多次锁,称为重入锁
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo06 implements Runnable {
private int tickCount = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
try{
if(tickCount<=0){
break;
}
System.out.println(Thread.currentThread().getName()+":"+tickCount);
tickCount--;
}finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(4);
Demo06 demo06 = new Demo06();
executorService.submit(demo06);
executorService.submit(demo06);
executorService.submit(demo06);
executorService.submit(demo06);
executorService.shutdown();
}
}
读写锁:
reentantreadwritelock
一种支持一写多读的同步锁,读写分离,可分别分配读锁,写锁
支持多次分配读锁,使用多个读操作可以并发执行。
互斥规则,
写一写,互斥,阻塞
读-写,互斥,读阻塞,写阻塞读
读一读,不互斥,不阻塞
在读操作远远高于写操作的环境中,可保障线程安全情况下,提高运行效率。
package com.wangjunji.qifeng.threadpool;
import java.security.Security;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
public class Demo07 {
private ReentrantReadWriteLock rr1 = new ReentrantReadWriteLock();
private ReadLock readLock = rr1.readLock();
private WriteLock writeLock = rr1.writeLock();
private String value;
public String getValue(){
readLock.lock();
try{
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("读取value"+value);
return this.value;
} finally {
readLock.unlock();
}
}
public void setValue(String value){
writeLock.lock();
try{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("写入value"+value);
this.value = value;
}finally {
writeLock.unlock();
}
this.value = value;
}
public static void main(String[] args) {
Demo07 demo07 = new Demo07();
//创建20个的线程池
ExecutorService executorService = Executors.newFixedThreadPool(40);
for (int i = 0; i < 22; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
demo07.setValue(Thread.currentThread().getName()+new Random().nextInt(100));
}
});
}
for (int i = 0; i < 18; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
demo07.getValue();
}
});
}
executorService.shutdown();
//如果是true,则执行完成
executorService.isTerminated();
}
}
collection体系集合中,除vector以外的线程安全集合。
package com.wangjunji.qifeng.threadpool;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class Dem09 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList<>();
//使用collections中的线程安全方法转换线程安全的集合。
Collection strings = Collections.synchronizedCollection(arrayList);
//java并发包里面的
/*ArrayList arrayList = new ArrayList<>();
Collection strings = Collections.synchronizedCollection(arrayList);*/
CopyOnWriteArrayList strings = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int i1 = 0; i1 < 10; i1++) {
strings.add(Thread.currentThread().getName()+":"+temp+":"+i1);
System.out.println(strings.toString());
}
}
}).start();
}
}
}
线程安全的arraylist,加强版的读写分离
写有锁,读无锁,读写之间不阻塞,优于读写锁。
写入时,先copy一个容器副本,再添加新元素,最后替换引用。 以空间来换取安全。
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo10 {
public static void main(String[] args) {
CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
for (int i1 = 0; i1 < 100; i1++) {
list.add(i1);
}
}
});
}
//这个结束的时候,有可能所有的子任务还没有完成,需要增加以下的代码
executorService.shutdown();
System.out.println(list.size());
while (!executorService.isTerminated()){
}
System.out.println(list.size());
}
}
线程安全的Set,底层使用copyOnWriteArrayList实现
唯一不同在于,使用addifAbsent添加元素,会遍历数组
如果存在元素,则不添加(扔掉副本)
collection的子接口,表示队列Fifo()先进先出
栈,先进后出
常用方法:
都需要抛出异常
boolean add(E e):顺序添加一个元素(到达上限后,再添加则会抛出异常)
E remove 获得一个元素并移除(如果队列没有元素,则抛出异常)
E element() 获得一个元素,但不移除,如果队列没有元素,则抛出异常。
boolean offer(E e)//顺序添加一个元素(到达上限后,再添加则会返回false)
E poll获得一个元素并移除 (如果队列没有元素时,则返回null)
E peek() 获得一个元素但不移除 (如果队列没有元素时,则返回null)
package com.wangjunji.qifeng.threadpool;
import java.util.LinkedList;
import java.util.Queue;
public class Demo11 {
public static void main(String[] args) {
Queue fruits = new LinkedList<>();
fruits.offer("苹果");
fruits.offer("葡萄");
fruits.offer("香蕉");
fruits.offer("梨子");
fruits.offer("葡萄");
fruits.offer("苹果");
fruits.offer("葡萄");
fruits.offer("香蕉");
fruits.offer("梨子");
fruits.offer("葡萄");
System.out.println(fruits.size());
int length = fruits.size();
for (int i = 0; i < length; i++) {
System.out.println(fruits.poll());
}
}
}
线程安全、可高效读写的队列,高并发下性能最好的队列
无锁,cas 比较交换算法,修改的方法包含三个核心参数(Ven)
v要更新的变量
E预期值
N新值。
只有当v==e时,v=n,否则表示已被更新过,则取消当前操作。
Queue的子接口,阻塞队列,增加了两个线程状态为无限期的等待的方法。
方法
void put(E e)将指定的元素插入此队列中,如果没有可用空间,则等待
E take 获得并移除此队列的头部元素,如果没有可用元素,则等待。
可用于解决产生、消费的问题
ArrayBlockingQueue:
数组结构实现,有界队列(手工固定上限)
BlockingQueue
LinkedBlockingQueue
链表结构实现,有界队列(默认上限integer.Max_Value)
BlockingQueue
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
/**
* 阻塞队列的使用
* 案例1:创建一个有界队列,添加数据
* 案例2:使用阻塞队列实现生产者和消费者
*/
public class Demo13 {
public static void main(String[] args) throws Exception {
ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);
queue.put("aaa");
queue.put("aaa");
queue.put("aaa");
queue.put("aaa");
}
}
生产者,消费者
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.ArrayBlockingQueue;
public class Demo14 {
public static void main(String[] args) {
ArrayBlockingQueue queue = new ArrayBlockingQueue<>(6);
Thread zhengzheng = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("生产了第" + i + "个面包");
queue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread bingbing = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("消费" + queue.take() + "个面包" );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
zhengzheng.start();
bingbing.start();
}
}
ConcurrentHashMap:
初始容量默认为16段,使用分段锁设计
不对整个map加锁,而是为每个segment加锁
当多个对象存入同一个segment时,才需要互斥
最理想的状态为16个对象分别存入16个segemnt,并行数量为16.
使用方式与hashmap无异
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo15 {
public static void main(String[] args) {
ConcurrentHashMap hashMap = new ConcurrentHashMap();
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
for (int i1 = 0; i1 < 1000; i1++) {
hashMap.put(Thread.currentThread().getName()+":"+i1,"value"+ String.valueOf(Math.random()*1000));
}
}
});
}
executorService.shutdown();
while (!executorService.isTerminated()){
}
System.out.println(hashMap);
}
}