栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java并发

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java并发

超链接:
java并发
java并发2

一、几个概念

1.进程、线程

2.并行、并发

  • 并行:多个事件在同一时刻发生
  • 并发:多个事件在同一时间间隔发生

3.同步、异步

  • 同步:需要等待另一个线程的结果返回,才能继续运行
  • 异步:不需要等待另一个线程的结果,就可以继续运行
二、Java线程的创建、运行

默认,主线程

方法一,直接使用 Thread

匿名内部类,创建Thread的子类,重写run()方法

// 创建线程
// 构造方法的参数是给线程指定的名字
Thread t1 = new Thread("t1") {
 @Override
 // run 方法内实现了要执行的任务
 public void run() {
 log.debug("hello");
 }
};
// 启动线程
t1.start();
方法二,使用 Runnable 配合 Thread

把【线程】和【任务】(要执行的代码)分开

  • Thread 代表线程
  • Runnable 可运行的任务(线程要执行的代码)
// 创建任务对象
Runnable task2 = new Runnable() {
 @Override
 public void run() {
 log.debug("hello");
 }
};
// 参数1 是任务对象; 参数2 是线程名字,推荐给线程起个名字
Thread t2 = new Thread(task2, "t2");
t2.start();

lambda写法
() -> {},相当于:创建了子类/实现类,重写了方法,并new了一个对象。三步简化为一步。

Runnable task2 = () -> {log.debug("hello");}
方法三,FutureTask 配合 Thread

FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
使用get()得到线程返回的结果

public static void main(String[] args) {
        // 返回100
        FutureTask futureTask = new FutureTask<>(new Callable() {
            @Override
            public Integer call() throws Exception {
                log.debug("running...");
                Thread.sleep(1000);
                return 100;
            }
        });
        // 主线程会阻塞,等待线程执行完毕的结果
        Thread t = new Thread(futureTask,"t1").start();
        log.debug("主线程");
        log.debug("{}",futureTask.get());//主线程使用线程结果
        //{}是占位符,结果会拼在大括号的位置
    }
三、线程的运行原理

观察多个线程同时运行:
1.交替执行
2.多个线程的执行先后,不受程序员控制

栈、栈帧

JVM由堆、栈、方法区组成,栈由多个栈帧组成。
栈用来存放线程,栈帧用来存放方法。

程序计数器 pc

每个线程都有它自己的程序计数器,是线程私有的。
程序计数器是用于存放下一条指令所在单元的地址。

运行Testframes.java的整体流程:
1.类加载。将.class文件加载到jvm的方法区中。
2.启动栈。运行main主线程。
3.分配栈帧。运行主线程中的各个方法。

线程的上下文切换

因为一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程

四、线程的常用方法


join():等待另一个线程运行结束。结束后,可能去拿它的结果,用于线程间的通信。可以一直等待它结束,或者只等待一段时间。
yield():让出当前线程的CPU使用,类似于sleep()
数字大,优先级高
不推荐的方法:stop()停止线程、suspend()挂起/暂停线程、resume()恢复线程

sleep() 与 yield()

sleep()
1.调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
2.其它线程可以使用 interrupt 方法打断正在睡眠的线程,那么被打断的线程这时就会抛出 InterruptedException异常
3.睡眠结束后的线程未必会立刻执行
4.建议用 TimeUnit 的 sleep() 代替 Thread 的 sleep()来获得更好的可读性

yield()
1.调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态
2.yield()后的线程仍可能会继续执行

小结
yield使cpu调用其它线程,但是cpu可能会再分配时间片给该线程;而sleep需要等过了休眠时间之后才有可能被分配cpu时间片

interrupt()

1.打断 sleep,wait,join 的线程

2.打断正常运行的线程
打断正常运行的线程, 线程并不会暂停。
可以使用打断标记isInterred(),做别的处理。

sleep(),yiled(),wait(),join() 对比

sleep,join,yield,interrupted是Thread类中的方法
wait/notify是object中的方法

sleep 不释放锁、释放cpu
join 释放锁、抢占cpu
yiled 不释放锁、释放cpu
wait 释放锁、释放cpu

五、线程状态

说法一:5种状态
是从操作系统的层面划分的

说法二:6种状态
是从 Java API 的层面划分的
即根据 Thread.State 枚举,分为六种状态

new:创建了线程,但没调用start()方法
runnable:包括就绪、运行、阻塞(指IO阻塞)
blocked:synchronized锁,得不到锁而阻塞
waiting:join()
timed_waiting:sleep()
terminated:线程运行完毕

六、多线程共享资源 1. 共享资源 2. 临界区

可以让多线程对共享资源进行读写操作的代码块。

3. synchronized

即‘对象锁’
互斥
同一时刻最多只有一个线程持有对象锁。此时,拿着锁的此线程就可以访问临界区的代码。

语法:

synchronized(对象) // 线程1获得锁,那么线程2的状态是blocked
{
 临界区
}

synchronized原理:
synchronized实际上利用对象保证了临界区代码的原子性,临界区内的代码在外界看来是不可分割的,不会被线程切换所打断

图解:

同学1得到cpu的时间片,得到锁,进入。同学2、3得到cpu的时间片,得不到锁,blocked阻塞。注意,阻塞状态是不会分到cpu的时间片的。同学1的时间片用完,但他仍持有锁,直到再次得到时间片,继续执行。同学1执行完毕,释放锁,唤醒另外的同学…

过程式的代码

@Slf4j
public class Test1 {
    static int count = 0;
    static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                synchronized (lock) {
                    count++;
                }
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                synchronized (lock) {
                    count--;
                }
            }
        }, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        log.debug("{}", count);
    }
}

改进:使用面向对象思路的锁
让共享资源成为一个类,操作加锁。

@Slf4j
public class Test1 {
    public static void main(String[] args) throws InterruptedException {

        Room room = new Room();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                room.increment();
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                room.decrement();
            }
        }, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        log.debug("{}", room.getCount());
    }
}

class Room {
    private int count = 0;

    public void increment() {
        synchronized (this) {
            count++;
        }
    }

    public void decrement() {
        synchronized (this) {
            count--;
        }
    }

    public int getCount() { //获取也要加锁
        synchronized (this) {
            return count;
        }
    }

}

synchronized的补充写法
非静态方法:this对象锁

class Test {
    public synchronized void test() { }
}

//等价于
class Test {
    public void test() {
        synchronized (this) { }
    }
}

静态方法:类对象锁

class Test {
    public synchronized static void test() { }
}

// 等价于
class Test {
    public static void test() {
        synchronized (Test.class) { }
    }
}
管程——悲观锁(阻塞) JMM

原子性
可见性
有序性

无锁——乐观锁(非阻塞) 不可变 开发工具

线程池
JUC:
Lock
Semaphore
CountdownLatch
CyclicBarrier
ConcurrentHashMap
ConcurrentlinkedQueue
BlockingQueue
CopyOnWriteArrayList
disruptor
guava

异步编程

CompletetableFuture
反应式:
project-reactor
spring webflux

非共享模型

私有
Actor——弱一致性
akka

并行

函数式编程
并行编程:映射、归约

其它领域 原理 模式

Balking
Guarded Suspension
控制顺序
两阶段退出
WorkThread
Thread per Message
生产者/消费者

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/327513.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号