JAVA并发编程总结(1)

发布于:2022-12-21 ⋅ 阅读:(134) ⋅ 点赞:(0)

进程与线程

进程

程序由指令和数据组成,程序要运行就必须把指令加载至CPU,数据存到内存中。当一个程序被运行,从磁盘加载代码至内存就开启了一个进程。

线程

线程就是一个指令流,在CPU中按一定顺序执行。
一个进程中可以有多个线程。可以理解为线程是进程的子集。

JAVA线程

创建和运行线程

方法一:thread

 Thread thread1 =new Thread("t1"){
            public void run(){
                log.debug("线程一.....");
            }
        };
thread1.start();

方法二:Runnable配合Thread

 Runnable runnable =new Runnable() {
            @Override
            public void run() {
                log.debug("线程二.....");
            }
        };
         Thread thread2 =new Thread(runnable,"t2");
         thread2.start();

使用FutureTask 用来创建有返回值的线程

FutureTask<String>task=new FutureTask<>(()->{
            return "hello world";
        });
         new Thread(task,"t3").start();

Thread和Runnable的关系

thread把线程和方法结合在一起了,Runnable把方法和线程分开,
更容易和线程池和api结合。

常见方法

start() 启动一个新线程,但不会立即执行,等待CPU时间片分给他
run() 新线程启动会调用的方法
join() 等待线程运行结束
join(long n) 等待线程运行结束,最多等待n毫秒
getPriority() 获取线程优先级
setPriority() 设置线程优先级
interrupt() 打断先程(设置打断标记)
interrupted() 判断当前线程是否被打断(清除打断标记)
isInterrupted() 判断当前线程是否被打断(不会打断标记)
currentThread() 获取当前线程

start()和run()

1.run运行

Thread thread =new Thread("t1"){
            public void run(){
                log.debug("线程运行,,,");
            }
        };
        thread.run();
        log.debug("主线程运行");

结果:

在这里插入图片描述

2.start运行

 Thread thread =new Thread("t1"){
            public void run(){
                log.debug("线程运行,,,");
            }
        };
        thread.start();
        log.debug("主线程运行");

在这里插入图片描述

直接用run()没有启动新的线程,启用start()启用新的线程

join方法

思考一下下面代码运行结果是什么?

static int number =0;
    public static void main(String[] args) {
       test1();
    }
    public static void test1(){
     Thread t1 =new Thread(()->{
         log.debug("线程开始");
         try {
             Thread.sleep(1);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         number=10;
     },"t1");
     t1.start();
     log.debug("number={}",number);
    }

分析:
因为主线程和t1线程是并行执行的,t1线程1毫秒之后菜鸟算出number=10;
二主线程一开始就打印number
在这里插入图片描述
在t1.start()后加join运行结果为:
在这里插入图片描述
总结:join需要等待结果返回才能才能继续运行

interrupt

1.打断sleep,wait,join线程会使线程进入阻塞状态

Thread thread1=new Thread(()->{
    try {
        log.debug("线程开始");
        Thread.sleep(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
thread1.start();
Thread.sleep(1);
log.debug("打断状态:{}",thread1.isInterrupted());
thread1.interrupt();
        log.debug("打断状态:{}",thread1.isInterrupted());
    }

结果:
在这里插入图片描述
2.打断正常运行的线程不会清空打断状态

Thread thread1 =new Thread(()->{
            while (true){
                Thread current =Thread.currentThread();
                boolean interrupted = current.isInterrupted();
                if (interrupted){
                    log.debug("打断状态:{}",interrupted);
                    break;
                }
            }
        });
        thread1.start();
        Thread.sleep(1);
        thread1.interrupt();

结果:
在这里插入图片描述

3.打断pork线程不会清空打断状态

 Thread thread2 =new Thread(()->{
            log.debug("pork...");
            LockSupport.park();
            log.debug("打断状态:{}",Thread.currentThread().isInterrupted());
        });thread2.start();
        Thread.sleep(1);
        thread2.interrupt();

结果:
在这里插入图片描述

主线程与守护线程

默认情况下线程要运行结束才停止运行,而有一种特殊的线程叫守护线程,只有非守护线程运行结束,即使守护线程没有执行完也会强制结束。

共享模型之管程

synchronized

问题:
二个线程一个做自增1000次,一个自加1000次,结果为0吗?

 Thread thread1 =new Thread(()->{

            for (int i=0;i<5000;i++){
                number++;
            }
        },"t1");
        Thread thread2 =new Thread(()->{
            for (int i=0;i<5000;i++){
                number--;
            }
        },"t2");
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        log.debug("number=:{}",number);

以上结果为正,负,0,三种结果。因为JAVA对静态变量的自增自减不是原子操作。
多个线程访问共享资源并做读写操作时会出现问题,这段代码块称为临界区。

解决方案:
1.阻塞式的解决方案:synchronized,lock;
1.非阻塞式的解决方案:原子操作;

synchornized:即俗称的【对象锁】,它采用互斥的方式让同一
时刻至多只有一个线程能持有【对象锁】,其它线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换.

static  int number=0;
    public static Object room =new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 =new Thread(()->{
synchronized (room){
            for (int i=0;i<5000;i++){
                number++;
            }}
        },"t1");
        Thread thread2 =new Thread(()->{
            synchronized (room){
            for (int i=0;i<5000;i++){
                number--;
            }}
        },"t2");
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        log.debug("number=:{}",number);
    }

结果为0
synchronized好比一个房间,t1线程执行到synchronize(room)时,进入房间并锁了门,t2线程也执行到synchronize(room)只能在外面等待,防止上下文切换。

线程八锁

变量的线程安全

1.静态变量和成员变量没有共享则线程安全,如果被共享且有读写操作则要考虑线程安全问题。
2.局部变量线程安全,但局部变量引用的对象未必线程安全。
2.1如果该对象没有逃离方法的作用访问,它是线程安全的
2.2如果该对象逃离方法的作用范围,需要考虑线程安全

wait

api::
obj.wait() 让进入 object 监视器的线程到 waitSet 等待
obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒
obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒

 public static Object room=new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(()->{
            synchronized (room){
                log.debug("线程1执行...");
                try {
                    room.wait();//线程一直等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("其他代码");
            }

        },"t1");
        Thread thread2=new Thread(()->{
            synchronized (room){
                log.debug("线程2执行...");
                try {
                    room.wait();//线程一直等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("其他代码");
            }

        },"t2");
        thread1.start();
        thread2.start();
        Thread.sleep(2);
        synchronized (room){
            room.notify();//随机唤醒一个等待线程
           // room.notifyAll();// room.notifyAll();//唤醒所有等待线程
        }

sleep和wait的区别

  1. sleep 是 Thread 方法,而 wait 是 Object 的方法
  2. sleep 不需要强制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用
  3. sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁
 public static Object room =new Object();
    public static boolean food=false;
    public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(()->{
            synchronized (room){
                log.debug("有食物吗{}",food);
                if (!food){
                    log.debug("饿了,先歇会");
                    try {
                        Thread.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("有食物吗{}",food);
                if (food){
                    log.debug("可以干活了");
                }
            }
        },"t1");
        thread1.start();
        for (int i=0;i<5;i++){
            new Thread(()->{
                synchronized (room){
                    log.debug("其他线程...");
                }
            },"ts").start();
        }

        Thread.sleep(1);
        food=true;
    }

在这里插入图片描述
其它干活的线程,都要一直阻塞,效率太低

public static Object room =new Object();
    public static boolean food=false;
    public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(()->{
            synchronized (room){
                log.debug("有食物吗{}",food);
                if (!food){
                    log.debug("饿了,先歇会");
                    try {
                        room.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("有食物吗{}",food);
                if (food){
                    log.debug("可以干活了");
                }
            }
        },"t1");
        thread1.start();

        for (int i=0;i<5;i++){
            new Thread(()->{
                synchronized (room){
                    log.debug("其他线程...");
                }
            },"ts").start();
        }
        Thread.sleep(100);
        food=true;
        synchronized (room){
            room.notifyAll();
        }
    }

在这里插入图片描述

park和unpark

基本使用:

LockSupport.park();
LockSupport.unpark();

特点:
1)wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
2)park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等待线程,就不那么【精确】
3)park & unpark 可以先 unpark,而 wait & notify 不能先 notify

线程状态转换

在这里插入图片描述
情况 1 NEW --> RUNNABLE
当调用 t.start() 方法时,由 NEW --> RUNNABLE

情况 2 RUNNABLE <–> WAITING
t 线程用 synchronized(obj) 获取了对象锁后
调用 obj.wait() 方法时,t 线程从 RUNNABLE --> WAITING
调用 obj.notify() , obj.notifyAll() , t.interrupt() 时
竞争锁成功,t 线程从 WAITING --> RUNNABLE
竞争锁失败,t 线程从 WAITING --> BLOCKED

情况 3 RUNNABLE <–> WAITING
当前线程调用 t.join() 方法时,当前线程从 RUNNABLE --> WAITING
注意是当前线程在t 线程对象的监视器上等待
t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 WAITING --> RUNNABLE

情况 4 RUNNABLE <–> WAITING
当前线程调用 LockSupport.park() 方法会让当前线程从 RUNNABLE --> WAITING
调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,会让目标线程从 WAITING -->
RUNNABLE

情况 5 RUNNABLE <–> TIMED_WAITING
t 线程用 synchronized(obj) 获取了对象锁后
调用 obj.wait(long n) 方法时,t 线程从 RUNNABLE --> TIMED_WAITING
t 线程等待时间超过了 n 毫秒,或调用 obj.notify() , obj.notifyAll() , t.interrupt() 时
竞争锁成功,t 线程从 TIMED_WAITING --> RUNNABLE
竞争锁失败,t 线程从 TIMED_WAITING --> BLOCKED

情况 6 RUNNABLE <–> TIMED_WAITING
当前线程调用 t.join(long n) 方法时,当前线程从 RUNNABLE --> TIMED_WAITING
注意是当前线程在t 线程对象的监视器上等待
当前线程等待时间超过了 n 毫秒,或t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从TIMED_WAITING --> RUNNABLE

情况 7 RUNNABLE <–> TIMED_WAITING
当前线程调用 Thread.sleep(long n) ,当前线程从 RUNNABLE --> TIMED_WAITING
当前线程等待时间超过了 n 毫秒,当前线程从 TIMED_WAITING --> RUNNABLE

情况 8 RUNNABLE <–> TIMED_WAITING
当前线程调用 LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long millis) 时,当前线
程从 RUNNABLE --> TIMED_WAITING
调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,或是等待超时,会让目标线程从
TIMED_WAITING–> RUNNABLE

情况 9 RUNNABLE <–> BLOCKED
t 线程用 synchronized(obj) 获取了对象锁时如果竞争失败,从 RUNNABLE --> BLOCKED
持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上所有 BLOCKED 的线程重新竞争,如果其中 t 线程竞争
成功,从 BLOCKED --> RUNNABLE ,其它失败的线程仍然 BLOCKED

情况 10 RUNNABLE <–> TERMINATED
当前线程所有代码运行完毕,进入 TERMINATED

本文含有隐藏内容,请 开通VIP 后查看