进程和线程:
进程:一个进程可以包含多个线程,至少是一个线程,java默认2个线程——main、GC(垃圾回收)
线程:开了一个软件,写字的时候也会有自动保存的线程在进行,Thread、Runnable、Callable
java开启不了线程,java操作的是底层的C++,java无法操作,用的是虚拟机
并发与并行:
并发:多线程操作同一个资源(假设cpu只有一核的时候,模拟多线程,快速交替处理线程)
并行:CPU多核情况下才有,多个线程同时执行
并发编程的本质:充分利用电脑的CPU
线程有几种状态:6种:new(创建)、runnable(运行)、blocked(阻塞)、wait(等待)、timed_waiting(超时等待)
terminated(终止)
wait与sleep的区别:
1.来自不同的类——wait(Object)、sleep(Thread)
2.关于锁——wait会释放锁,而sleep不会释放
3.适用范围不一样——wait必须在同步代码块中执行,sleep则不用
4.w捕获异常——wait不需要捕获异常、sleep需要捕获异常
传统Synchronized解决有顺序卖票问题
package JUC;
/*基本的卖票的例子
线程是一个单独的资源类
* */
public class Demo02SaleTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"C").start();
}
}
class Ticket{
private int num=30;
public synchronized void sale(){
if (num>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"票,剩余"+num);
}
}
}
package JUC;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo03Lock {
public static void main(String[] args) {
Ticket1 ticket1 = new Ticket1();
}
}
//Lock锁————三步骤——1.new ReentrantLock 2.加锁 3.解锁
class Ticket1{
private int num=30;
//ReentrantLock---java默认非公平锁,也就是默认可以插队(比较合理,3秒的不需要等待3小时的线程)
Lock lock=new ReentrantLock();
public void sale(){
lock.lock();//加锁
try {
//业务代码
if (num>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"票,剩余"+num);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();//解锁
}
}
}
package JUC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*JUC版的生产者与消费者问题
通过Lock可以找到Condition
以下的是无序的随机的状态结果,JUC的优势还没有体现出来
ReentrantLock是可重入的意思
* */
public class Demo05JUC1 {
public static void main(String[] args) {
Date1 date1 = new Date1();
new Thread(()->{for (int i = 0; i < 10; i++) {
try {
date1.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{for (int i = 0; i < 10; i++) {
try {
date1.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{for (int i = 0; i < 10; i++) {
try {
date1.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{for (int i = 0; i < 10; i++) {
try {
date1.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Date1{
private int num=0;
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (num!=0){
//等待
//this.wait();JUC之前普通的
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+"=="+num);
//通知线程B,我+1完毕了this.notifyAll();JUC之前普通的
condition.signalAll();//唤醒全部
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (num==0){
//等待
//this.wait();
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+"=="+num);
//通知线程B,我-1完毕了 JUC之前的this.notifyAll();
condition.signalAll();//唤醒全部
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
package JUC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*实现生产者与消费者的有序JUC执行
三条线程按照循环顺序执行
* */
public class Demo06JUC2 {
public static void main(String[] args) {
new Thread(()->{},"A").start();
new Thread(()->{},"B").start();
new Thread(()->{},"C").start();
}
}
//资源类
class Date3{
private Lock lock=new ReentrantLock();
private Condition condition = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int num=1;
public void print1(){
lock.lock();
try {//业务代码,判断-执行-通知
if (num!=1){
condition.await();
}
System.out.println(Thread.currentThread().getName()+"1111");
//通知唤醒
num=2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print2(){
lock.lock();
try {
if (num!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"2222");
num=3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print3(){
lock.lock();
try {
if (num!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"3333");
num=1;
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
package JUC;
import java.util.concurrent.TimeUnit;
/*什么是锁,锁的对象是谁——查看自己的收藏笔记
8锁,就是关于锁的8个问题
1.标准情况下,线程先执行哪个?————先发短信再打电话(原因:锁的存在)
2.发短信延迟4秒的情况下————还是先短信再电话,先执行发短信,因为有同步锁
3.增加了一个普通方法后,是执行发短信还是hello,限制性普通方法,普通方法不受锁的限制
4.两个对象的情况下,先执行发短信还是打电话——————打电话,因为两把锁不一样,打电话的锁有延迟
5.静态方法的时候,两个线程先执行哪个?——————加了static以后锁的就是class对象,类的模板只有一个所以和原先锁的是对象没有关系
* */
public class Demo07suo1 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
//第一条线程
new Thread(()->{
phone.send();
},"A").start();
//A线程睡眠
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//第二条线程
new Thread(()->{
phone1.call();
},"B").start();
}
}
//资源类
class Phone{
public static synchronized void send(){ //synchronized锁的是方法的调用者,两个功能都是phone的调用,锁的对象是phone
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
(ConcurrentModificationException并发修改异常,不能一起修改)
package JUC;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/*.ConcurrentModificationException并发修改异常,不能一起修改
并发下ArrayList不安全————结局方案:
1.List list1 =new Vector<>();
2. List list1 = Collections.synchronizedList(new ArrayList<>());
3. List list1 = new CopyOnWriteArrayList<>();它比Vector效率高原因在于Vector用到了synchronized,导致了效率低
4.
* */
public class Demo11Unsafe {
public static void main(String[] args) {
List<String> list1 = new CopyOnWriteArrayList<>();
/* CopyOnWriteArrayList写入时复制COW计算机程序设计领域的一种优化策略
在写入的时候复制一份,出现覆盖的问题,造成数据问题
*/
for (int i = 0; i < 10; i++) {
list1.add(UUID.randomUUID().toString().substring(0,5));//生成随机字符串
new Thread(()->{
System.out.println(list1);
},String.valueOf(i)).start();//添加十条线程
}
}
}
package JUC;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/*Set还是会出现并发问题
HashSet的底层就是hashmap
add set 本质就是map ,map key是无法重复的
解决方案:
1.Set set = Collections.synchronizedSet(new HashSet<>());
2.Set set =new CopyOnWriteArraySet<>();
3.
* */
public class Demo12SetTest {
public static void main(String[] args) {
// Set set = Collections.synchronizedSet(new HashSet<>());
new HashSet<>();
Set<String> set =new CopyOnWriteArraySet<>();
for (int i = 1; i < 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
package JUC;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/*HashMap本质也不是安全的
解决方法:
1.Map map1 = new ConcurrentHashMap<>();
2.ConcurrentHashMap原理
* */
public class Demo13HashMapTest {
public static void main(String[] args) {
Map<String , String > map = new HashMap<>();
Map<String , String > map1 = new ConcurrentHashMap<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
package JUC;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/*加法计数器
线程达到一定数量的时候开始执行操作,集齐7颗龙珠就可以召唤神龙
* */
public class Demo15CyclicBarrier {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("完成加法计数");
});
for (int i = 1; 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();
}
}
}
package JUC;
import java.util.concurrent.CountDownLatch;
/*JUC常用三大类:一、
*计数器(减法计数器)
*
* */
public class Demo14CountDownLatch {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);//总数是6,必须要执行任务的时候再使用
for (int i = 1; i <= 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"go");
countDownLatch.countDown();//数量-1
},String.valueOf(i)).start();
}
countDownLatch.await();//等待计数器归0,然后再向下执行
System.out.println("close");
}
}
#12线程等待释放离开、等待一定时间停车位释放(Semaphore)
package JUC;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/*信号量
线程数量:停车位,有限的线程分给几个对象相互占用,限流————控制线程数
semaphore.acquire()————获得,假设线程满了就等待释放
semaphore.release()————释放
* */
public class Demo16Semaphore {
public static void main(String[] args) {
//线程数量:停车位
Semaphore semaphore = new Semaphore(3);
for (int i = 1; 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();//释放
}
},String.valueOf(i)).start();
}
}
}
package JUC;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*读写锁
读锁(共享锁)多个线程可以同时占有
写锁(独占锁)一次只能被一个线程锁占用
* */
public class Demo17ReentrantReadWriteLock {
public static void main(String[] args) {
MyCache myCache = new MyCache();
//写入的操作
for (int i = 1; i <= 5; i++) {
final int a=i;//lambda表达式无法访问外部的变量
new Thread(()->{
myCache.put(a+"",a+"");
},String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
final int a=i;//lambda表达式无法访问外部的变量
new Thread(()->{
myCache.get(a+"");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private volatile Map<String ,Object> 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()+"写入完毕");
} 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(Thread.currentThread().getName()+"读取完毕");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
添加、删除、队列首元素、超时等待等等方法
package JUC;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
/*BlockingQueue四组API,queue是队列的意思
添加、删除、队列首元素、超市等待等等方法
* */
public class Demo18BlockingQueue {
public static void main(String[] args) {
/* test1();
test2();*/
try {
test3();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void test1(){//满了会抛出异常
//队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
System.out.println(blockingQueue.element());
// System.out.println(blockingQueue.add("d"));
//取出元素
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//System.out.println(blockingQueue.remove());.NoSuchElementException异常
// System.out.println(blockingQueue.remove());
}
public static void test2(){//满了不会抛出异常
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("d"));//false,不会抛出异常
System.out.println(blockingQueue.peek());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());//不会阻塞,直接显示空的值
}
public static void test3() throws InterruptedException {
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
blockingQueue.put("d");//现在的这种情况就是一直的等待,因为已经满了,所以要等待移除出去以后才可以开始添加
blockingQueue.take();
blockingQueue.take();
blockingQueue.take();
blockingQueue.take();//没有元素的时候也是一直阻塞等待的
}
public static void test4() throws InterruptedException {
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
blockingQueue.offer("d",3, TimeUnit.SECONDS);//超时等待,超过3秒就退出
blockingQueue.poll(2,TimeUnit.SECONDS);
blockingQueue.poll(2,TimeUnit.SECONDS);
blockingQueue.poll(2,TimeUnit.SECONDS);
blockingQueue.poll(2,TimeUnit.SECONDS);//取出,超过两秒就退出
}
}
package JUC;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/*同步队列
和其他的BlockingQueue不一样,SynchronousQueue不存储元素
put了一个值,必须从里面先take出来,否则不能再put进去
* */
public class Demo19SynchronousQueue {
public static void main(String[] args) {
BlockingQueue<String > blockingQueue = new SynchronousQueue<>();//同步队列
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName()+"put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName()+"put 3");
blockingQueue.put("3");
} catch (Exception e) {
e.printStackTrace();
} finally {
}
},"A").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=="+blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=="+blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=="+blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
}
}
/*线程池(重点)
线程池的好处:
1.降低资源的消耗
2.提高相应的速度
3.方便管理
线程三大方法、7大参数、4种拒绝策略(重点!!!)
3大方法:
以下Executors三大方法
7大参数:
ThreadPoolExecutor线程池执行者包含的7个参数
4种拒绝策略:
1.new ThreadPoolExecutor.AbortPolicy() 人满了,抛出异常
2.new ThreadPoolExecutor.CallerRunsPolicy(),main的线程执行,哪来的去哪里
3.new ThreadPoolExecutor.DiscardPolicy()线程满了,丢掉任务,不会抛出异常
4.new ThreadPoolExecutor.DiscardOldestPolicy());线程满了先和最早的线程去竞争,然后争不过的额时候、丢掉任务,不会抛出异常
使用了线程池以后就要用线程池来创建线程
线程池的大小怎么去设置:(两种)
CPU密集型:电脑是几核的就用几来定义最大线程数
IO密集型:当你的电脑有大型的任务的时候,设置线程大于IO密集型任务的个数(IO)十分占用资源
package JUC;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo20pool {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newSingleThreadExecutor();//单线程
//ExecutorService threadPool = Executors.newFixedThreadPool(5);//设定固定的线程数
//ExecutorService threadPool = Executors.newCachedThreadPool();//可变化的线程数量,遇强则强,遇弱则弱
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());//两个窗口、最多5个窗口、等待3秒、候客区3个、默认线程、拒绝策略(四种)
try {
for (int i = 0; i < 9; i++) {
threadPoolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完需要关闭
threadPoolExecutor.shutdown();
}
}
}
package ceshi;
public class CPU {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
函数式四大接口:Consumer、Function、Predicate、Supplier
程序员必备技能:lambda表达式、链式编程、函数式接口、Stream流式计算
package JUC;
import java.util.function.Function;
/*四大函数式接口:Function
* */
public class Demo21Function {
public static void main(String[] args) {
//工具类:输入输出的值相等
Function function = new Function<String,String>() {
@Override
public String apply(String str) {return str;}};
System.out.println(function.apply("asd"));
Function<String , String > function1 = (str)->{return str;};//最简化lambda表达式
}
}
package JUC;
import java.util.function.Predicate;
/*四大函数式接口:Predicate
断定型接口:有一个输入参数、返回值只能是布尔值
* */
public class Demo22Predicate {
public static void main(String[] args) {
//判断字符串是否为空
/* Predicate predicate = new Predicate() {
@Override
public boolean test(String o) {
return o.isEmpty();
}
};
*/
Predicate<String> predicate = (str)->{return str.isEmpty();};//lambda表达式
System.out.println(predicate.test("asd"));
}
}
package JUC;
import java.util.function.Consumer;
/*消费型接口:只有输入值,没有返回值
* */
public class Demo23Consumer {
public static void main(String[] args) {
/* Consumer consumer = new Consumer() {
@Override
public void accept(String o) {
System.out.println(o);
}
};*/
Consumer<String > consumer =(o)->{System.out.println(o);};
consumer.accept("byug");
}
}
package JUC;
import java.util.function.Supplier;
/*供给型接口:没有参数、只有返回值
* */
public class Demo24Supplier {
public static void main(String[] args) {
/* Supplier objectSupplier = new Supplier() {
@Override
public Integer get() {
System.out.println("get()");
return 1024;
}
};*/
Supplier<Integer> supplier =()->{System.out.println("get()");return 1024;};
System.out.println(supplier.get());
}
}
package JUC;
import java.util.Arrays;
import java.util.List;
/*链式编程
* */
public class Demo25 {
public static void main(String[] args) {
User user = new User(1,"a",21);
User user2 = new User(2,"b",23);
User user3 = new User(3,"c",45);
User user4 = new User(4,"d",48);
//集合就是存储
List<User> list = Arrays.asList(user, user2, user3, user4);
//计算交给Stream
//lambda表达式、链式编程、函数式接口、Stream流式计算
//以下就是链式编程
list.stream()
.filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>23;})
.map(u->{return u.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1)
.forEach(System.out::println);
}
}
class User{
int id;
String name;
int age;
public User() {
}
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
特点:并行执行任务、提高效率、大数量的时候使用(注意有弊端,必须大数据的时候去使用)
ForkJoin特点:工作窃取,如图所示,当A没有完成任务的时候,B线程已经完成任务,B线程就会去窃取A线程的任务,从而达到有效利用的目的。
ForkJoin缺点:因为是双端队列,所以可能AB会同时去抢任务。所以必须大量数据的时候去使用
双端队列:可以从上往下执行,也可以从下往上执行
package JUC;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;
/*线程拆分
*
* */
public class Demo26ForkJoin {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// test1();//2000
// test2();//2000
test3();//200
}
//测试一
public static void test1(){
long sum=0L;
long start=System.currentTimeMillis();
for (long i = 1L; i <= 10_0000_0000; i++) {
sum+=i;
}
long end=System.currentTimeMillis();
System.out.println("和为"+sum+",花掉的时间为:"+(end-start));
}
//测试二
public static void test2() throws ExecutionException, InterruptedException {
long start=System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinDemo task = new ForkJoinDemo(0L,10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
Long sum = submit.get();
long end=System.currentTimeMillis();
System.out.println("和为"+sum+",花掉的时间为:"+(end-start));
}
//测试三
public static void test3(){
long start=System.currentTimeMillis();
//Stream并行流 rangeClosed(],parallel是并行的意思,
long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
long end=System.currentTimeMillis();
System.out.println("和为"+sum+",花掉的时间为:"+(end-start));
}
}
/*
1.forkjoin通过它来执行
2.计算任务forkjoinpool.execute(ForkjoinTest task)
* */
class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
//临界值
private Long temp=100L;
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
public void test(){
}
//计算方法
@Override
protected Long compute() {
if ((end-start)>temp){
//分值计算
Long sum=0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else{
long middle = (start + end) / 2;//中间值
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
task1.fork();//拆分任务,把任务压入线程队列
ForkJoinDemo task2= new ForkJoinDemo(middle+1, end);
task2.fork();//拆分任务,把任务压入线程队列
return task1.join()+task2.join();
}
}
}
Future:对将来的某个时间的结果进行建模
package JUC;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/*异步调用Future
Ajax:不需要等待一段时间得到前面的线程结果,而后可以直接去进行别的任务
异步执行
成功回调
失败回调
* */
public class Demo27 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
/* //发起一个请求 runAsync(异步)
//以下是没有返回值的runAsync异步回调
CompletableFuture completableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
});
System.out.println("1111");
completableFuture.get();//获取阻塞执行结果*/
//以下是有返回值的supplyAsync异步回调
//ajax,成功和失败的回调,如果失败返回的是错误的信息
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
int i=10/0;
return 1024;
});
System.out.println(completableFuture.whenComplete((t, u) -> {
System.out.println("t" + t);//正常执行返回结果
System.out.println("u" + u);//有错误的时候返回的错误信息
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return 100;//可以获取到错误的返回信息
}).get());
}
}
Volatile:java虚拟机提供轻量级的同步机制
1.保证可见性
2.不保证原子性
3.禁止指令重排
JMM:java内存模型,不存在的东西,概念、约定
作用:保证线程的安全
关于JMM的一些同步约定:
1.线程加锁前,必须把共享变量刷回主存(内存里面有一份,线程拷贝得出结果以后刷回主存)
2.线程解锁前,必须读取主存中的最新值到工作内存中
3.加锁和解锁必须是同一把锁
工作内存、主内存八种操作,必须成对存在
package JUC;
import java.util.concurrent.TimeUnit;
public class Demo28JMM {
private static int num=0;
public static void main(String[] args) {//main主线程
new Thread(()->{//线程1
while (num==0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num=1;
System.out.println(num);
}
}
运行结果是1,但是程序没有停止
需要让A线程知道主内存中的值已经发生变化,这个时候就要用到Volatile
Volatile不保证原子性
原子性:不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割,要么同时成功,要么同时失败
package JUC;
/*不保证原子性
* */
public class Demo29Volatile {
private static int num=0;//volatile加上以后不能保证20000次执行结果,所以volatile不保证原子性!!
public static void add(){//synchronized加上以后可以保证20000次执行,
num++;//num++本身就不是一个原子性操作
}
public static void main(String[] args) {
//理论上的结果为20000
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int i1 = 1; i1 <= 1000; i1++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//activeCount是还存活的线程的数量,除开main和gc以外才可以0
Thread.yield();//礼让
}
System.out.println(Thread.currentThread().getName()+" "+num);
}
}
package JUC;
import java.util.concurrent.atomic.AtomicInteger;
/*不保证原子性
* */
public class Demo29Volatile {
//原子类的Integer
private static AtomicInteger num=new AtomicInteger();//volatile加上以后不能保证20000次执行结果,所以volatile不保证原子性!!
public static void add(){//synchronized加上以后可以保证20000次执行,
//num++;//num++本身就不是一个原子性操作
/* getAndIncrement +1方法 这个方法本质是使用了CAS,跟底层操作系统弄挂钩,所以效率比较高,在内存中修改值
* 它是一个很特殊的存在*/
num.getAndIncrement();
}
public static void main(String[] args) {
//理论上的结果为20000
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int i1 = 1; i1 <= 1000; i1++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//activeCount是还存活的线程的数量,除开main和gc以外才可以0
Thread.yield();//礼让
}
System.out.println(Thread.currentThread().getName()+" "+num);
}
}
/指令重排
什么是指令重排?————你写的程序,计算机并不是按照那样子去执行
源代码>编译器优化重排>指令并行可能也会重排>内存系统可能也会重排>执行
处理器在进行指令重排的时候,会考虑数据之间的依赖性
因为指令重排会导致结果错误,所以Volatile可以避免指令重排导致的错误结果
只要加了Volatile就会产生;
内存屏障CPU指令,作用***:1.可以特定的操作的顺序2.可以保证某些变量的内存可见性
(利用这些特性Volatile实现了可见性)
内存屏障的作用:中间产生内存屏障,禁止上下的指令顺序交换
Volatile可以保持可见性、不能保证原子性,但是由于内存屏障,可以保证避免出现指令重排的现象产生
Volatile在单例模式在玩的最多
单例模式:
package JUC;
/*懒汉式单例
* */
public class Demo32LazyMan {
private Demo32LazyMan() {
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static Demo32LazyMan demo32LazyMan;//避免出现指令重拍错误结果,必须加Volatile
public static Demo32LazyMan getInstance(){
//加锁的操作,双重检测锁模式的懒汉式单例——DCL懒汉式
if (demo32LazyMan == null) {
synchronized (Demo32LazyMan.class){
if (demo32LazyMan==null){
demo32LazyMan=new Demo32LazyMan(); //不是一个原子性操作
/*1.分配内存空间
2. 执行构造方法,初始化对象
3.把这个对象指向这个空间
* */
}
}
}
return demo32LazyMan;
}
//单线程下单例ok,多线程下有问题,所以需要加锁
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
demo32LazyMan.getInstance();
}).start();
}
}
}
/*什么是CAS?
比较当前工作内存中的值,如果这个值是期望的,那么执行操作,如果不是那么就一直循环
CAS:compareAndSet:比较并交换
CAS:ABA问题:(狸猫换太子)
缺点:
1.循环会耗时
2.一次性只能保证一个共享变量的原子性
3.会产生ABA问题
Unasfe类可以让java操作内存,java正常是不可以操作内存的,C++可以,所以它相当于java的后门
两条线程同时操作的时候,第二条线程第一次把3修改成1,但是对于第一条线程来说,原来的她已经不再是原来的她,虽然看起来是一样(狸猫换太子)
package ceshi;
import java.util.concurrent.atomic.AtomicReference;
public class Demo01 {
//ABA问题
public static void main(String [] args) throws InterruptedException {
AtomicReference<String> ref=new AtomicReference<String>("A");
String prev=ref.get();
new Thread(()-> {
System.out.println("change A->B "+ref.compareAndSet("A", "B"));
//盲区,do sth
System.out.println("change B->A "+ref.compareAndSet("B", "A"));
}).start();
Thread.sleep(1000);
System.out.println("change A->C "+ref.compareAndSet(prev, "C"));
}
}
package ceshi;
import java.util.concurrent.atomic.AtomicStampedReference;
public class Demo02 {
//解决ABA问题
/*步骤:
1.new AtomicStampedReference (新建原子戳引用)
2.ref.getReference(); (获得参考)
3.ref.getStamp() (获得印章)
4.new Thread (创建线程,输出原子引用实例AB之间的转换)
5.ref.compareAndSet(prev, "C",mstamp,1) (输出盖章为目标转换到C的结果)
* */
public static void main(String [] args) throws InterruptedException {
AtomicStampedReference<String> ref=new AtomicStampedReference<String>("A",0);
String prev=ref.getReference();
int mstamp=ref.getStamp();
new Thread(()-> {
int stamp=ref.getStamp();
System.out.println("change A->B "+ref.compareAndSet("A", "B",stamp,1));
//不再是盲区 do sth unknown.....
stamp=ref.getStamp();
System.out.println("change B->A "+ref.compareAndSet("B", "A",stamp,1));
}).start();
Thread.sleep(1000);
System.out.println("change A->C "+ref.compareAndSet(prev, "C",mstamp,1));
}
}
各种锁的理解:
1.公平锁与非公平锁
公平锁:公平,不可以插队
非公平锁:不公平,可以插队,默认都是非公平锁(因为比较公平)
2.可重入锁(递归锁):
package JUC;
import java.util.concurrent.atomic.AtomicReference;
/*自旋锁
* */
public class Demo38SpinLock {
AtomicReference<Thread> atomicReference=new AtomicReference<>();
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"myLock");
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"myUnLock");
atomicReference.compareAndSet(thread,null);
}
}
package JUC;
import java.util.concurrent.TimeUnit;
public class Demo39TestSpinLock {
public static void main(String[] args) throws InterruptedException {
Demo38SpinLock lock = new Demo38SpinLock();
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"T1").start();
TimeUnit.SECONDS.sleep(3);
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"T2").start();
}
}
package JUC;
import java.util.concurrent.TimeUnit;
/*死锁的情况
两个对象互相都想拿对方的锁
解决办法:
使用jps定位进程号
* */
public class Demo40Lock {
public static void main(String[] args) {
String lockA="lockA";
String lockB="lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"=>get"+lockA);
}
}
}
}
1.使用jps -l定位进程号
运行之后Terminal输入jps -l就会查处具体的线程信息
2.使用jstack查看进程信息
输入jstack 14212
得到以下的结果
这个问题可以自行查询~