当前位置: 首页 > article >正文

Java-并发基础

启动线程的方式

只有:

1、X extends Thread;,然后X.start

2、X implements Runnable;然后交给Thread运行

有争议可以可以查看 Thread源码的注释:

There are two ways to create a new thread of execution.

Callable的方式需要塞进FutureTaskFutureTask最终也是继承了Runnable

线程的状态

Java中线程的状态分为6种:

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
    线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

  3. 阻塞(BLOCKED):表示线程阻塞于锁。

  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。1

  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

  6. 终止(TERMINATED):表示该线程已经执行完毕。

只有sync才是阻塞状态,拿不到锁被迫阻塞

死锁的四个必要条件

1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。

2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

预防死锁

打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。

打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。

打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。

打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。

避免死锁常见的算法有有序资源分配法、银行家算法

  • 解决死锁案例:

import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *类说明:演示尝试拿锁解决死锁
 */
public class TryLock {
    private static Lock lock1 = new ReentrantLock();//第一个锁
    private static Lock lock2 = new ReentrantLock();//第二个锁

    //先尝试拿lock1 锁,再尝试拿lock2锁,lock2锁没拿到,连同lock1 锁一起释放掉
    private static void fisrtToSecond() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        Random r = new Random();
        while(true){
            if(lock1.tryLock()){
                System.out.println(threadName
                        +" get lock1");
                try{
                    if(lock2.tryLock()){
                        try{
                            System.out.println(threadName
                                    +" get lock2");
                            System.out.println("fisrtToSecond do work------------");
                            break;
                        }finally{
                            lock2.unlock();
                        }
                    }
                }finally {
                    lock1.unlock();
                }

            }
            Thread.sleep(r.nextInt(3));
        }
    }

    //先尝试拿lock2锁,再尝试拿lock1锁,lock1锁没拿到,连同lock2锁一起释放掉
    private static void SecondToFisrt() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        Random r = new Random();
        while(true){
            if(lock2.tryLock()){
                System.out.println(threadName
                        +" get lock2");
                try{
                    if(lock1.tryLock()){
                        try{
                            System.out.println(threadName
                                    +" get lock1");
                            System.out.println("SecondToFisrt do work------------");
                            break;
                        }finally{
                            lock1.unlock();
                        }
                    }
                }finally {
                    lock2.unlock();
                }

            }
            Thread.sleep(r.nextInt(3));
        }
    }

    private static class TestThread extends Thread{

        private String name;

        public TestThread(String name) {
            this.name = name;
        }

        public void run(){
            Thread.currentThread().setName(name);
            try {
                SecondToFisrt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread.currentThread().setName("TestDeadLock");
        TestThread testThread = new TestThread("SubTestThread");
        testThread.start();
        try {
            fisrtToSecond();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

上述案例为啥加了 Thread.sleep(r.nextInt(3));

不加会有活锁问题。加了可以缓解活锁。就是两个线程都在拿锁,但是都没拿到,拿了很多次才拿到

活锁

两个线程在尝试拿锁的机制中,发生多个线程之间互相谦让,不断发生同一个线程总是拿到同一把锁,在尝试拿另一把锁时因为拿不到,而将本来已经持有的锁释放的过程。

解决办法:每个线程休眠随机数,错开拿锁的时间。

ThreadLocal

与Synchonized的比较

ThreadLocal和Synchonized都用于解决多线程并发訪问。可是ThreadLocal与synchronized有本质的差别。synchronized是利用锁的机制,使变量或代码块在某一时该仅仅能被一个线程訪问。而ThreadLocal为每个线程都提供了变量的副本,使得每个线程在某一时间訪问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享。

如何隔离的数据?

每个Thread中都存着一个ThreadLocalMap

ThreadLocal.ThreadLocalMap threadLocals = null;

查看ThreadLocalsetcreateMap函数

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

上述代码可知当前 ThreadThreadLocalMap就绑定在一起了

查看 ThreadLocalget函数

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

通过get可以看到 getMap获取的是当前ThreadThreadLocalMap,也就跟其他线程无关了
ThreadLocalMap里面有 Entry就是用来存放数据的

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}
为什么不直接搞个Map存一下数据?

import java.util.HashMap;
import java.util.Map;

/**
 * 类说明:自己实现的ThreadLocal
 */
public class MyThreadLocal<T> {
    /*存放变量副本的map容器,以Thread为键,变量副本为value*/
    private Map<Thread,T> threadTMap = new HashMap<>();

    public synchronized T get(){
        return  threadTMap.get(Thread.currentThread());
    }

    public synchronized void set(T t){
        threadTMap.put(Thread.currentThread(),t);
    }

}

直接用一个Map虽然可以隔离,但是压力都给到了Map上,在多线程时对这个Map会有激烈的竞争


http://www.kler.cn/news/330826.html

相关文章:

  • Nacos笔记
  • 论文笔记(四十七)Diffusion Policy: Visuomotor Policy
  • 【C++复习】C++11经典语法
  • 将模板引擎用于 Express
  • 65 注意力分数_by《李沐:动手学深度学习v2》pytorch版
  • 前端开发技术框架选型
  • 每日一题|1928. 规定时间内到达终点的最小花费|动态规划、最小路径
  • 强弱依赖(含示例)
  • ANTLR4 与 flex/bision、lex/yacc 的比较
  • Electron 进程通信
  • Spring MVC系统学习(二)——Spring MVC的核心类和注解
  • 五子棋双人对战项目(5)——对战模块
  • Ubuntu编译fftw3
  • 端口隔离配置的实验
  • ElasticSearch学习笔记(三)Ubuntu 2204 server elasticsearch集群配置
  • JavaCV 实现视频链接截取封面工具
  • 掌控物体运动艺术:图扑 Easing 函数实践应用
  • 【Linux 从基础到进阶】Cassandra数据库安装与调优
  • SpringBoot与微服务:网上租赁系统的现代化构建
  • CSS——文字闪烁效果