※快速掌握java多线程
一.基本概念
(一)程序、进程与线程
- 程序是静态的概念,windows下通常指exe文件。
- 进程是动态的概念,是程序在运行状态,进程说明程 序在内存中的边界。
- 线程是进程内的一个”基本任务”,每个线程都有自己 的功能,是CPU分配与调度的基本单位。
(二)并行与并发
并行是线程在多喝cpu上运行。
并发就是指程序同时处理多个任务的能力, 并发编程的根源在于对多任务情况下对访问资源的有 效控制。在CPU来回切换
(三)同步与异步
同步是指你做了某件事,这件事还没结束,你就必须等待完成。
异步是指你做了某件事,这件事还没结束,你可以去做别的事情先
(四)临界区
临界区用来表示一种公共资源与共享数据,可以被多 个线程使用。
同一时间只能有一个线程访问临界区(阻塞状态), 其他资源必须等待。
(五)死锁、饥饿、活锁
死锁:你在等我释放资源,我也等你释放资源,大家互不相让。
饥饿:一直无法获取足够资源的线程状态
活锁:谁都互相礼让所要的资源
(六)线程安全
在拥有共享数据的多条线程并行执行的程序中,线程 安全的代码会通过同步机制保证各个线程都可以正常 且正确的执行,不会出现数据污染等意外情况。
(七)线程安全三大特性
原子性 – 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何 因素打断,要么就都不执行。i = i + 1
可见性 – 当多个线程访问同一个变量时,一个线程修改了这个变量的值,其 他线程能够立即看得到修改的值。
有序性 – 如果在本线程内观察,所有的操作都是有序的;如果在一个线程观 察另一个线程,所有的操作都是无序的。
(八)java内存模型
(九)volatile重排序
(十)线程的5种状态
二.创建多线程(主线程+垃圾回收线程+自己创建的)
(一)继承Thread
public class Match1 {
public static void main(String[] args) {
Runner liuxiang = new Runner();//创建一个新的线程
liuxiang.setName("刘翔");//设置线程名称
Runner laoqi = new Runner();
laoqi.setName("老齐");
Runner op = new Runner();
op.setName("路飞");
liuxiang.start();//启动线程
laoqi.start();
op.start();
}
}
class Runner extends Thread{
@Override
public void run() {
Integer speed = new Random().nextInt(100);
for(int i = 1 ; i <= 100 ; i++){
try {
Thread.sleep(1000); //当前线程休眠1秒
}catch (Exception e){
e.printStackTrace();
}
//this.getName()打印当前线程的名字
System.out.println(this.getName() + "已前进" + (i * speed) + "米(" + speed + "米/秒)");
}
}
}
(二)实现Runnable接口
public class Match2 {
public static void main(String[] args) {
Runner2 liuxiang = new Runner2();
Thread thread1 = new Thread(liuxiang);
thread1.setName("刘翔");
Thread laoqi = new Thread(new Runner2());
laoqi.setName("老齐");
Thread op = new Thread(new Runner2());
op.setName("路飞");
thread1.start();
laoqi.start();
op.start();
}
}
class Runner2 implements Runnable {
@Override
public void run() {
Integer speed = new Random().nextInt(100);
for(int i = 1 ; i <= 100 ; i++){
try {
Thread.sleep(1000); //当前线程休眠1秒
}catch (Exception e){
e.printStackTrace();
}
//Thread.currentThread()用于获取当前执行的线程对象
//在Runnable中是无法使用this获取到当前线程对象的
System.out.println(Thread.currentThread().getName() + "已前进" + (i * speed) + "米(" + speed + "米/秒)");
}
}
}
(三)继承Callable接口
package thread;
import java.util.Random;
import java.util.concurrent.*;
public class Match3 {
public static void main(String[] args) throws Exception {
Runner3 liuxiang = new Runner3();//实例化Callable对象
liuxiang.setName("刘翔");
final Integer call = liuxiang.call();
Runner3 laoqi = new Runner3();
laoqi.setName("老齐");
Runner3 op = new Runner3();
op.setName("路飞");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Runner3 implements Callable<Integer>{
private String name ;
public void setName(String name){
this.name = name;
}
//实现Callable接口可以允许我们的线程返回值或抛出异常
@Override
public Integer call() throws Exception {
Integer speed = new Random().nextInt(100);
Integer distince = 0; //总共奔跑的距离
for(int i = 1 ; i <= 100 ; i++){
Thread.sleep(10);
distince = i * speed;
System.out.println(this.name + "已前进" + distince + "米(" + speed + "米/秒)");
}
return distince;
}
}
(四)继承Callable接口+线程池管理
package thread;
import java.util.Random;
import java.util.concurrent.*;
public class Match3 {
public static void main(String[] args) throws Exception {
//创建一个线程池。里面天生有3个“空”线程。Executors是调度器,对线程池进行管理
ExecutorService executorService = Executors.newFixedThreadPool(3);
Runner3 liuxiang = new Runner3();//实例化Callable对象
liuxiang.setName("刘翔");
Runner3 laoqi = new Runner3();
laoqi.setName("老齐");
Runner3 op = new Runner3();
op.setName("路飞");
//将这个对象扔到线程池中,线程池自动分配一个线程来运行liuxiang这个对象的call方法
//Future用于接受线程内部call方法的返回值
Future<Integer> result1 = executorService.submit(liuxiang);
Future<Integer> result2 = executorService.submit(laoqi);
Future<Integer> result3 = executorService.submit(op);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// executorService.shutdown();//关闭线程池释放所有资源
System.out.println("刘翔累计跑了" + result1.get() + "米" );
System.out.println("老齐累计跑了" + result2.get() + "米" );
System.out.println("路飞累计跑了" + result3.get() + "米" );
}
}
class Runner3 implements Callable<Integer>{
private String name ;
public void setName(String name){
this.name = name;
}
//实现Callable接口可以允许我们的线程返回值或抛出异常
@Override
public Integer call() throws Exception {
Integer speed = new Random().nextInt(100);
Integer distince = 0; //总共奔跑的距离
for(int i = 1 ; i <= 100 ; i++){
Thread.sleep(10);
distince = i * speed;
System.out.println(this.name + "已前进" + distince + "米(" + speed + "米/秒)");
}
return distince;
}
}
(五)三种方式比较
三.线程同步(控制线程的执行顺序)
(一)synchronized代码块 - 任意对象即可
public class SyncSample {
public static void main(String[] args) {
Couplet c = new Couplet();
for(int i = 0 ; i < 10000 ; i++){
new Thread(){
public void run(){
int r = new Random().nextInt(2);
if(r % 2 == 0){
Couplet.first();
}else{
Couplet.second();
}
}
}.start();
}
}
}
class Couplet{
Object lock = new Object(); //锁对象
public void first(){
synchronized (lock) { //同步代码块,在同一时间只允许有一个线程执行访问这个方法
System.out.printf("琴");
System.out.printf("瑟");
System.out.printf("琵");
System.out.printf("琶");
System.out.println();
}
}
public void second(){
synchronized (lock) { //因为两个同步代码指向了同一把锁lock,所以在同一个时间内只允许有一个代码块执行,其他等待
System.out.printf("魑");
System.out.printf("魅");
System.out.printf("魍");
System.out.printf("魉");
System.out.println();
}
}
}
(二)synchronized方法 - this当前对象
public class SyncSample {
public static void main(String[] args) {
Couplet c = new Couplet();
for(int i = 0 ; i < 10000 ; i++){
new Thread(){
public void run(){
int r = new Random().nextInt(2);
if(r % 2 == 0){
Couplet.first();
}else{
Couplet.second();
}
}
}.start();
}
}
}
class Couplet{
public synchronized void first(){
{
System.out.printf("琴");
System.out.printf("瑟");
System.out.printf("琵");
System.out.printf("琶");
System.out.println();
}
public synchronized void second(){
System.out.printf("魑");
System.out.printf("魅");
System.out.printf("魍");
System.out.printf("魉");
System.out.println();
}
}
}
(三)synchronized静态方法 - 该类的字节码对象
package thread;
import java.util.Random;
public class SyncSample {
public static void main(String[] args) {
Couplet c = new Couplet();
for(int i = 0 ; i < 10000 ; i++){
new Thread(){
public void run(){
int r = new Random().nextInt(2);
if(r % 2 == 0){
Couplet.first();
}else{
Couplet.second();
}
}
}.start();
}
}
}
class Couplet{
public synchronized static void first(){
// synchronized (lock) { //同步代码块,在同一时间只允许有一个线程执行访问这个方法
System.out.printf("琴");
System.out.printf("瑟");
System.out.printf("琵");
System.out.printf("琶");
System.out.println();
// }
}
public static void second(){
synchronized (Couplet.class) { //因为两个同步代码指向了同一把锁lock,所以在同一个时间内只允许有一个代码块执行,其他等待
System.out.printf("魑");
System.out.printf("魅");
System.out.printf("魍");
System.out.printf("魉");
System.out.println();
}
}
}
四.死锁
(一)死锁的原因及措施
(二)死锁演示
package thread;
public class DeadLock {
private static String fileA = "A文件";
private static String fileB = "B文件";
public static void main(String[] args) {
new Thread(){ //线程1
public void run(){
while(true) {
synchronized (fileA) {//打开文件A,线程独占
System.out.println(this.getName() + ":文件A写入");
synchronized (fileB) {
System.out.println(this.getName() + ":文件B写入");
}
System.out.println(this.getName() + ":所有文件保存");
}
}
}
}.start();
new Thread(){ //线程2
public void run(){
while(true) {
synchronized (fileB) {//打开文件A,线程独占
System.out.println(this.getName() + ":文件B写入");
synchronized (fileA) {
System.out.println(this.getName() + ":文件A写入");
}
System.out.println(this.getName() + ":所有文件保存");
}
}
}
}.start();
}
}
五.线程安全(单线程和多线程执行结果相同)
(一)
(二)
(三)解决线程不安全方法:
利用上面线程同步的方法解决
六.JDK并发工具包
(一)概述
并发是伴随着多核处理器的诞生而产生的,为了充分 利用硬件资源,诞生了多线程技术。但是多线程又存 在资源竞争的问题,引发了同步和互斥的问题,JDK 1.5推出的java.util.concurrent(并发工具包)来解决 这些问题。
(二)线程池
在java.util.concurrent中,提供了工具类Executors(调 度器)对象来创建线程池,可创建的线程池有四种:
-
CachedThreadPool - 可缓存线程池
package juc; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolSample1 { public static void main(String[] args) { //调度器对象 //ExecutorService用于管理线程池 ExecutorService threadPool = Executors.newCachedThreadPool();//创建一个可缓存线程池 //可缓存线程池的特点是,无限大,如果线程池中没有可用的线程则创建,有空闲线程则利用起来 for(int i = 1 ; i <= 1000 ; i++) { final int index = i; threadPool.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ":" + index); } }); } try { Thread.sleep(1000); //跟线程足够的运行时间 } catch (InterruptedException e) { e.printStackTrace(); } threadPool.shutdown(); } }
-
FixedThreadPool - 定长线程池
package juc;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolSample2 { public static void main(String[] args) { //调度器对象 //ExecutorService用于管理线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10);//创建一个可创建一个定长线程池 //定长线程池的特点是固定线程总数,空间线程用于执行任务,如果线程都在使用后续任务则处于等待状态,在线程池中的线程 //如果任务处于等待的状态,备选的等待算法默认为FIFO(先进先出) LIFO(后进先出) //执行任务后再执行后续的任务。 for(int i = 1 ; i <= 1000 ; i++) { final int index = i; threadPool.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ":" + index); } }); } try { Thread.sleep(1000); //跟线程足够的运行时间 } catch (InterruptedException e) { e.printStackTrace(); } threadPool.shutdown(); } }
- SingleThreadExecutor - 单线程池
package juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolSample3 {
public static void main(String[] args) {
//调度器对象
//ExecutorService用于管理线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();//单线程线程池
for(int i = 1 ; i <= 1000 ; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + index);
}
});
}
try {
Thread.sleep(1000); //跟线程足够的运行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
//shutdown() 代表关闭线程池(等待所有线程完成)
//shutdownNow() 代表立即终止线程池的运行,不等待线程,不推荐使用
threadPool.shutdown();
}
}
-
ScheduledThreadPool - 调度线程池
import java.util.Date; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ThreadPoolSample4 { public static void main(String[] args) { ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);//可调度线程池 /*//延迟三秒执行一次Run方法 scheduledThreadPool.schedule(new Runnable() { @Override public void run() { System.out.println("延迟3秒执行"); } } , 3 , TimeUnit.SECONDS);*/ //Timer , 项目实际开发中scheduledThreadPool与Timer都不会用到,因为有成熟的调度框架Quartz,或者Spring自带调度, //成熟的调度框架支持一种表达式叫做Cron表达式,有兴趣的童鞋可以了解一下。 scheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println(new Date() + "延迟1秒执行,每三秒执行一次"); } }, 1, 3, TimeUnit.SECONDS); } }
(三)CountDownLatch - 倒计时锁
-
CountDownLatch倒计时锁特别适合”总-分任务”,
例如多线程计算后的数据汇总CountDownLatch类位于java.util.concurrent
(J.U.C)包下,利用它可以实现类似计数器的功能。 比如有一个任务A,它要等待其他3个任务执行完毕之
后才能执行,此时就可以利用CountDownLatch来实 现这种功能了。
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CountDownSample { private static int count = 0; public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(100); CountDownLatch cdl = new CountDownLatch(10000); //CDL总数和操作数保持一致 for(int i = 1 ; i <= 10000 ; i++) { final int index = i; threadPool.execute(new Runnable() { @Override public void run() { synchronized (CountDownSample.class) { try { count = count + index; //计数器减一 }catch(Exception e){ e.printStackTrace(); }finally { cdl.countDown(); } } } }); } /* try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }*/ try { cdl.await(); //堵塞当前线程,知道cdl=0的时候再继续往下走 //为了避免程序一直挂起,我们可以设置一个timeout时间 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(count); threadPool.shutdown(); } }
(四)JUC之Semaphore信号量
信号量表示同一个资源能同时被多少人使用。
package juc;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreSample1 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
Semaphore semaphore = new Semaphore(5);//定义5个信号量,也就是说服务器只允许5个人在里面玩
for(int i = 1 ; i <= 20 ; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();//获取一个信号量,“占用一个跑到”
play();
semaphore.release();//执行完成后释放这个信号量,“从跑道出去”
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
threadPool.shutdown();
}
public static void play(){
try {
System.out.println(new Date() + " " + Thread.currentThread().getName() + ":获得紫禁之巅服务器进入资格");
Thread.sleep(2000);
System.out.println(new Date() + " " + Thread.currentThread().getName() + ":退出服务器");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
设定没有获取资源的时候提示信息
package juc;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreSample2 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
Semaphore semaphore = new Semaphore(5);//定义5个信号量,也就是说服务器只允许5个人在里面玩
for(int i = 1 ; i <= 20 ; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
//尝试获取一次信号量,5秒钟内获取到返回true,否则返回false,也可以不设置参数,表示立刻不行就算
if(semaphore.tryAcquire(6, TimeUnit.SECONDS)) {
play();
semaphore.release();//执行完成后释放这个信号量,“从跑道出去”
}else{
System.out.println(Thread.currentThread().getName() + ":对不起,服务器已满,请稍后再试");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
threadPool.shutdown();
}
public static void play(){
try {
System.out.println(new Date() + " " + Thread.currentThread().getName() + ":获得紫禁之巅服务器进入资格");
Thread.sleep(2000);
System.out.println(new Date() + " " + Thread.currentThread().getName() + ":退出服务器");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(五)JUC之CyclicBarrier循环屏障
-
CyclicBarrier是一个同步工具类,它允许一组线程互相等 待,直到到达某个公共屏障点。与CountDownLatch不同的是该barrier在释放等待线程后可以重用,所以称它为循 环(Cyclic)的屏障(Barrier)
public class CyclicBarrierSample { private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5); public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for(int i = 1 ; i<=20 ; i++) { final int index = i; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } executorService.execute(new Runnable() { @Override public void run() { go(); } }); } executorService.shutdown(); } private static void go(){ System.out.println(Thread.currentThread().getName() + ":准备就绪" ); try { cyclicBarrier.await();//设置屏障点,当累计5个线程都准备好后,才运行后面的代码 System.out.println(Thread.currentThread().getName() + ":开始运行"); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }
}
(六)JUC之ReentrantLock重入锁
重入锁是指任意线程在获取到锁之后,再次获取该锁而不会被该锁 所阻塞
ReentrantLock设计的目标是用来替代synchronized关键字(可惜最后不是)
public class ReentrantLockSample {
public static int users = 100;//同时模拟的并发访问用户数量
public static int downTotal = 50000; //用户下载的真实总数
public static int count = 0 ;//计数器
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(users);
for(int i = 0 ; i < downTotal ; i++){
executorService.execute(()->{
//通过多线程模拟N个用户并发访问并下载
try { semaphore.acquire();add();semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
});
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();//关闭调度服务
System.out.println(“下载总数:” + count);
}
public static void add(){
lock.lock();//上锁
try {
count++;
}finally {
lock.unlock(); //解锁,一定要放在finally里面否则会出现死锁
}
}
(六)JUC之Condition等待与唤醒
package juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionSample {
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition1 = reentrantLock.newCondition();
Condition condition2 = reentrantLock.newCondition();
Condition condition3 = reentrantLock.newCondition();
new Thread(new Runnable() {
@Override
public void run() {
reentrantLock.lock();
try {
condition1.await();
Thread.sleep(1000);
System.out.println("身");
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
reentrantLock.unlock();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
reentrantLock.lock();
try {
condition2.await();
Thread.sleep(1000);
System.out.println("体");
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
reentrantLock.unlock();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
reentrantLock.lock();
try {
condition3.await();
Thread.sleep(1000);
System.out.println("健");
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
reentrantLock.unlock();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
reentrantLock.lock();
try {
Thread.sleep(1000);
System.out.println("康");
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
reentrantLock.unlock();
}
}
}).start();
}
}
(七)JUC之Callable与Future.
package juc;
import java.util.concurrent.*;
public class FutureSample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for(int i = 2 ; i <= 10000 ; i++){
Computor c = new Computor();
c.setNum(i);
//Future是对用于计算的线程进行监听,因为计算是在其他线程中执行的,所以这个返回结果的过程是异步的
Future<Boolean> result = executorService.submit(c);//将c对象提交给线程池,如有空闲线程立即执行里面的call方法
try {
Boolean r = result.get(); //用于获取返回值,如果线程内部的call没有执行完成,则进入等待状态,直到计算完成
if(r == true){
System.out.println(c.getNum());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executorService.shutdown();
}
}
class Computor implements Callable<Boolean>{
private Integer num;
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
@Override
public Boolean call() throws Exception {
boolean isprime = true;
for(int i = 2 ; i < num ; i++) {
if (num % i == 0) {
isprime = false;
break;
}
}
return isprime;
}
}
(八)线程安全又效率高的容器
-
ArrayList -> CopyOnWriteArrayList - 写复制列表
-
package juc;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteArrayListSample {
public static void main(String[] args) {
//写复制列表
List list = new CopyOnWriteArrayList<>();
for(int i = 0 ; i < 1000 ; i++){
list.add(i);
}
Iterator itr = list.iterator();
while (itr.hasNext()) {
Integer i = itr.next();
list.remove(i);
}
System.out.println(list);
}
} -
HashSet -> CopyOnWriteArraySet - 写复制集合
-
HashMap -> ConcurrentHashMap - 分段锁映射
package juc;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class ConcurrentHashMapSample {
public static int users = 100;//同时模拟的并发访问用户数量
public static int downTotal = 50000; //用户下载的真实总数
public static ConcurrentHashMap count = new ConcurrentHashMap() ;//计数器
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(users);
for(int i = 0 ; i < downTotal ; i++){
final Integer index = i;
executorService.execute(()->{
//通过多线程模拟N个用户并发访问并下载
try {
semaphore.acquire();
count.put(index, index);
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
});
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();//关闭调度服务
System.out.println("下载总数:" + count.size());
}
}
(九)原子包
Atomic包是java.util.concurrent下的另一个专门为线程安全 设计的Java包,
包含多个原子操作类。
Atomic常用类
– AtomicInteger – AtomicIntegerArray – AtomicBoolean – AtomicLong – AtomicLongArray
Atomic的应用场景 u 虽然基于CAS的线程安全机制很好很高效,但要说的 是,并非所有线程安全都可以用这样的方法来实现, 这只适合一些粒度比较小型,如计数器这样的需求用起 来才有效,否则也不会有锁的存在了。
package juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerSample {
public static int users = 100;//同时模拟的并发访问用户数量
public static int downTotal = 50000; //用户下载的真实总数
public static AtomicInteger count = new AtomicInteger() ;//计数器
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(users);
for(int i = 0 ; i < downTotal ; i++){
executorService.execute(()->{
try { semaphore.acquire();add();semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
});
}
try {Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();//关闭调度服务
System.out.println("下载总数:" + count);
}
//用了哪个数据类型后变成线程安全
public static void add(){
count.getAndIncrement(); //count++
}
}