zoukankan      html  css  js  c++  java
  • Java并发编程基础篇

    1.1进程与线程

    进程:系统进行资源分配和调度的基本单位

    线程:CPU分配的基本单位

    一个进程包含很多个线程

    1.2线程的创建和运行

    三种方式:实现Runnable接口、继承Thread类、使用FutureTask方式(实现Callable接口中的call方法)

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class CallerTask implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "您好啊";
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
            FutureTask<String> futureTask = new FutureTask<String>(new CallerTask());
            new Thread(futureTask).start();
    
            String s = futureTask.get();
    
            System.out.println(s.toString());
        }
    }

    1.3线程通知与等待

    线程的状态:

    new:就绪,就是等待执行

    runnable:执行

    waiting:在等待队列里面

    timewaiting:有时间的等待

    blocked:阻塞状态

    terminate:执行完毕或者中断了,反正就是终了

    wait函数

    手动实现消费者和生产者-----不太会,就写了一个脑残的版本!

    import collection.Fu;
    import com.sun.xml.internal.bind.v2.model.annotation.RuntimeAnnotationReader;
    
    import java.util.LinkedList;
    import java.util.Queue;
    
    public class ProducerAndConsumer{
    
        public Queue<Integer> queue = new LinkedList<>();
        public int MAX_SIZE = 10;
    
    
        public class Producer1 implements Runnable{
            @Override
            public void run() {
                synchronized (queue){
                    while (true){
                        while (queue.size() == MAX_SIZE){
                            try {
                                System.out.println("做好了");
                                queue.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        queue.add(1);
                        System.out.println("做了一个");
                        queue.notify();
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
    
        public class Consumer1 implements Runnable{
            @Override
            public void run() {
                synchronized (queue){
                    while (true){
                        while (queue.size() == 0){
                            try {
                                System.out.println("吃完了" + queue.size());
                                queue.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        queue.remove();
                        System.out.println("吃了一个");
                        queue.notify();
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        public static void main(String[] args) {
    
            ProducerAndConsumer procon = new ProducerAndConsumer();
            Producer1 producer1 = procon.new Producer1();
            Consumer1 consumer1 = procon.new Consumer1();
            Thread t_p = new Thread(producer1);
            Thread t_c = new Thread(consumer1);
            t_c.start();
            t_p.start();
        }
    }

    wait(long timeout)函数

    public class WaitTimeOut {
        public static void main(String[] args) {
            String lock = "锁";
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock){
                        System.out.println("我开始了");
                        try {
                            lock.wait(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("我好了");
                    }
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock){
                        System.out.println("我直接冲");
                    }
                }
            }).start();
        }
    }
    /**
     * 我开始了
     * 我直接冲
     * 我好了
     */

    wait(long timeout, int nanos)函数

    后面的nanos其实没什么用,就是nanos>0时,timeout + 1;没什么实际作用的

    notify()函数

    看上面的消费者与生产者例子:

    当线程进入wait队列之后,在别的线程中,对象.notify()。就会在以这个对象为锁的wait队列里头唤醒一条线程。

    notifyAll()函数

    唤醒所有的wait队列里头的线程,然后再进行相互竞争锁的局面。害,其实我觉得没什么用的。

    join()函数

    在main线程中写:线程1.join()

    表示:将main线程加到线程1的末尾,所以只有线程1执行完成之后,main线程才能执行。

    sleep()函数

    简单的让线程睡一会

    yield函数

    当前线程让出cpu,但是cpu不一定会让出去,只是想让罢了;

    区别:

    除了sleep、yield、join可以在随便一个调用外,其它的只能在同步代码快里头使用。没有竞争,哪里来的wait和唤醒呢。

    线程中断

    void interrupt()

    随便一个地方中断线程,也就是给线程设置一下中断标志;如果此时线程在wait、sleep、join;那就最好了,直接抛出异常返回了。

    boolean isInterrupted()

    判断当前线程的中断标志为是否为真:是,返回true;否:返回false

    boolean interrupted()

    判断当前线程是否被中断,如果是返回true,然后清除中断标志!!!!,否就直接返回false;其实感觉并没有什么用;

    public class InterruptedTest{
        public static void main(String[] args) {
            String lock = "锁住";
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        System.out.println("兄弟,我来了");
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            System.out.println("我被杀了");
                        }
                    }
                }
            });
    
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("我是杀手,t1你去死吧");
                    t1.interrupt();
    
                }
            });
            t1.start();
            t2.start();
        }
    
    
    }

    线程上下文切换

    就是切来切去呗;

    线程死锁

    条件:

    • 互斥条件,就是需要竞争资源
    • 请求并持有条件:就是要了还想要
    • 不可剥夺条件:资源使用完之前不能剥夺
    • 环路等待条件:必须形成一个线程资源的环形链

    避免死锁:

    • 破坏请求并持有
    • 破坏环路等待

    守护线程和用户线程

    守护线程

    jvm的线程

    用户线程

    自己写的线程

    手写线程.setDaemon(true):设置成守护线程

    ThreadLocal

     创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免线程安全问题。

    创建一个ThreadLocal变量后,每个线程都会复制一个变量到自己的本地内存

    public class ThreadLocalTest {
    
        static void print(String str){
            //但因当前线程本地内存中的localVariable变量的值
            System.out.println(str + ":" + localVariable.get());
        }
    
        //创建ThreadLcoal变量
        static ThreadLocal<String> localVariable = new ThreadLocal<>();
    
        public static void main(String[] args) {
    
            //创建线程one
            Thread threadOne = new Thread(new Runnable() {
                @Override
                public void run() {
                    //设置线程one中的本地变量localVariable的值
                    localVariable.set("one");
                    print("threadOne");
                    System.out.println("one remove after" + ":" + localVariable.get());
                }
            });
    
            //创建线程one
            Thread threadTwo = new Thread(new Runnable() {
                @Override
                public void run() {
                    //设置线程one中的本地变量localVariable的值
                    localVariable.set("two");
                    print("threadTwo");
                    System.out.println("two remove after" + ":" + localVariable.get());
                }
            });
    
            threadOne.start();
            threadTwo.start();
        }
    }
    
    /**
     * threadOne:haha
     * threadTwo:two
     * two remove after:two
     * one remove after:haha
     */

     

     Thread类中有一个threadLocals和inheritableThreadLocals,它们是ThreadLocalMap类型的变量,而ThreadLocalMap是一个定制化的HashMap。

    默认情况下,这两个变量都为null,只有当前线程第一次调用ThreadLocal的set或者get方法时才会创建它们。

    其实每个线程的本地变量不是存放在ThreadLocal的实例里面,而是存放在调用线程的ThreadLocals变量里面。

    也就是说,ThreadLocal类型的本地变量放在具体的线程空间里面。ThreadLocal类就是也给工具壳,它通过set方法把value值放入调用线程的threadLocals里面并存放起来;当调用的线程用get方法时,再从当前线程的threadLocals里面拿出来。

    说了那么多:简单说就是:每个线程中有一个ThreadLocals变量:存放着很多ThreadLocal变量;threadLocals结构:hashMap<threadLocal, value>;

    set的步骤

    (1)获取当前的线程

    (2)获取当前线程的threadLocals,也就是那个map

    (3)map不为null,就将 <threadLocal的hash值作为key,值>,存入threadLocals中

    (4)map为空的,那好说了,就创建一个,并进行(3)

     get步骤:其实就是将threadLocal的hash值作为key,在threadLocals的map中查找,如果这源码看不懂,那也没办法了。

    这个就是,如果get操作时,发现threadlocals为空,那就是找不到!那么就初始化threadLocals!!!!!!!

    然后,这次没找到;那么将《threadlocal的hash值,null》插入到threadLocals中,不懂这样做有什么意义,骗自己嘛?

    都这样了,就是threadlocals的map删除threalocal

    不会真有人看不懂ThreadLocal的源码吧。好吧,我也是看了好久。

    主要难点是没搞清楚:

    Thread类中的threadLocals对应好多的ThreadLocal

    还有这一句:

    Thread t = Thread.currentThread();

    怎么获取当前线程的?其实很简单,因为threadLocal.set()是在该线程的run方法中执行的,你说能不能获得当前的线程是哪个呢?

    ThreadLocal不支持继承性

    就是简单说,每个线程中的ThreadLocal都不是互通的!其实废话了!

    但是你不会忘记了

    见名知意:这个东东应该就是继承用的!

    当然就有那么一个InheriatableThreadLocal类和inheritableThreadLocals对应咯

    其实方法都差不多的,就是后者重写了ThreadLocal中的一些方法;简单说就是创建子线程的时候,会将父亲的inhereatableThreadLocals中的本地变量赋值到子线程中。

    将父亲的inheriatableThreadLocal的赋值卸载儿子的后面结果:

    public class ThreadLocalTest {
    
        public static void main(String[] args) {
    
    
            InheritableThreadLocal<String> fu1 = new InheritableThreadLocal<>();
            InheritableThreadLocal<String> fu2 = new InheritableThreadLocal<>();
    
            InheritableThreadLocal<String> zi1 = new InheritableThreadLocal<>();
            InheritableThreadLocal<String> zi2 = new InheritableThreadLocal<>();
            //创建线程one
            Thread threadOne = new Thread(new Runnable() {
                @Override
                public void run() {
                    //设置线程one中的本地变量localVariable的值
                    zi1.set("儿子1");
                    zi2.set("儿子2");
    
                    System.out.println(zi1.get());
                    System.out.println(zi2.get());
    
                    System.out.println(fu1.get());
                    System.out.println(fu2.get());
                }
            });
    
            fu1.set("爸爸1");
            fu2.set("爸爸1");
    
            threadOne.start();
        }
    }
    
    /**结果
     * 儿子1
     * 儿子2
     * null
     * null
     */

    将父亲的inheriatableThreadLocal的赋值卸载儿子的前面结果:

    public class ThreadLocalTest {
    
        public static void main(String[] args) {
    
    
            InheritableThreadLocal<String> fu1 = new InheritableThreadLocal<>();
            InheritableThreadLocal<String> fu2 = new InheritableThreadLocal<>();
    
            InheritableThreadLocal<String> zi1 = new InheritableThreadLocal<>();
            InheritableThreadLocal<String> zi2 = new InheritableThreadLocal<>();
    
            fu1.set("爸爸1");
            fu2.set("爸爸1");
            //创建线程one
            Thread threadOne = new Thread(new Runnable() {
                @Override
                public void run() {
                    //设置线程one中的本地变量localVariable的值
                    zi1.set("儿子1");
                    zi2.set("儿子2");
    
                    System.out.println(zi1.get());
                    System.out.println(zi2.get());
    
                    System.out.println(fu1.get());
                    System.out.println(fu2.get());
                }
            });
    
    
    
            threadOne.start();
        }
    }
    
    /**结果
     * 儿子1
     * 儿子2
     * 爸爸1
     * 爸爸1
     */

    那么可以总结了:也就是子线程的inheritable初始化的那么一下将父线程的inheriatableThreadLocals的值复制一下,之后就不再保持一致了。

  • 相关阅读:
    bootstrap轮播组件之“如何关闭自动轮播”
    js分享功能
    设置省略号的取巧方法
    bootstrap-table表格插件的使用案例
    如果有帮到您,欢迎打赏
    IDEA导入Eclipse项目
    Centos nginx安装
    centos tomcat安装
    centos安装jdk
    Centos创建用户
  • 原文地址:https://www.cnblogs.com/sicheng-li/p/13200132.html
Copyright © 2011-2022 走看看