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的值复制一下,之后就不再保持一致了。

原文地址:https://www.cnblogs.com/sicheng-li/p/13200132.html