线程基础

需要了解程序会有线程安全问题, 必须要知道程序在jvm中是怎么运行的,运行的时候怎么就导致了线程安全问题了。
一、JVM运行的时的数据区域
jvm运行的数据区域分为:方法区、堆内存、虚拟机栈、本地方法栈、程序计数器。其中方法区和堆内存是线程共享的数据区域,虚拟机栈和本地方法栈、程序技术器是线程独占数据区域。如下图所示:
在这里插入图片描述
线程独占:每个线程都会有他的独立空间,随着线程的生命周期而创建和销毁
线程共享:所有线程都能访问的内存区域,随着虚拟机或Gc而创建和销毁
这里的线程共享就是多个线程同时访问的内存区域,通常情况下都会有出现线程安全问题。多线程的所有可能出现的问题都是基于访问这块内存区域导致的。Jdk和jvm都会提供不同的方法来解决多线安全问题。这个之后我们讨论。
图中的每个区域代表的意思,我们来了解下:
方法区:jvm用来存储加载的类的信息、常量、静态变量、编译后的代码等数据。其中不同虚拟机实现不同。jd7的方法区放在永久代,jdk8放在元数据区域。
堆内存:jvm启动的时候创建的,是存放绝大数对象实例的。可以细分为老年代、新生代(Eden、From survivor、To Survivor)
虚拟机栈:每个线程都会有一个隐私空间。多线程由多个栈帧组成。一个线程会执行一个或多个方法,一个方法对应一个栈帧。俗称栈内存
本地方法栈:是虚拟机使用native方法准备的
程序计数器:记录当前线程执行字节码的位置。cpu同一时间,会执行一个线程的指令。cpu切换执行的时候需要记录当前cpu程序的位置,便于下次继续执行。
由于程序在运行的时候类的实例会存在堆内存,堆内存时共享区域的,多个线程同时访问内存同一快内存变量,会导致取数或写数的不一致性,这样就会导致程序执行结果不正确。
二、了解线程
线程是是操作系统能够进行运算调度的最小单位,与进程不同。同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)等。这是计算机操作系统的定义。
线程在jdk中是怎么定义的:

  /**
     * Returns the state of this thread.
     * This method is designed for use in monitoring of the system state,
     * not for synchronization control.
     * @return this thread's state.
     * @since 1.5
     */
    public State getState() {
        // get current thread state
        return sun.misc.VM.toThreadState(threadStatus);
    }
    
 public static State toThreadState(int var0) {
        if ((var0 & 4) != 0) {
            return State.RUNNABLE;
        } else if ((var0 & 1024) != 0) {
            return State.BLOCKED;
        } else if ((var0 & 16) != 0) {
            return State.WAITING;
        } else if ((var0 & 32) != 0) {
            return State.TIMED_WAITING;
        } else if ((var0 & 2) != 0) {
            return State.TERMINATED;
        } else {
            return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
        }
    }

该方法用于监控系统状态,不用于同步控制。在vm.class中toThreadState方法中定义了6种状态。分别如下:RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED、NEW
线程状态的6个状态
1、new:上午启动线程的状态
2、runnable:可运行在线程的线程状态,等待cpu调用(tread.start())
cpu正在调用或则等待被调用。因为调用start,cpu要有时间去执行
3、blocked:线程阻塞等待的状态,处于synchronized同步代码块或方法中被阻塞
4、waiting:等待线程的站状态,不带超时wait,thread.join,locksuppor.park
依赖其他线程调度的
5、Timewaiting:具体等待时间的线程等待线程的状态,带超时
Thread.sleep;wait、Thread.jooin,locksupprot.parkNanos,locksupport.parkutil
6、terminated:终止线程的状态。线程正常结束或异常状态。
线程状态的切换:
线程中的join()可以用来邀请其他线程先执行。
yield()告诉系统"把自己的CPU时间让掉,让其他线程或者自己运行"
在这里插入图片描述
线程的中止interrupt、阻塞状态wait/notify的。

 /**
     * Interrupts this thread.
     *
     * <p> Unless the current thread is interrupting itself, which is
     * always permitted, the {@link #checkAccess() checkAccess} method
     * of this thread is invoked, which may cause a {@link
     * SecurityException} to be thrown.
     *
     * <p> If this thread is blocked in an invocation of the {@link
     * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
     * Object#wait(long, int) wait(long, int)} methods of the {@link Object}
     * class, or of the {@link #join()}, {@link #join(long)}, {@link
     * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
     * methods of this class, then its interrupt status will be cleared and it
     * will receive an {@link InterruptedException}.
     *
     * <p> If this thread is blocked in an I/O operation upon an {@link
     * java.nio.channels.InterruptibleChannel InterruptibleChannel}
     * then the channel will be closed, the thread's interrupt
     * status will be set, and the thread will receive a {@link
     * java.nio.channels.ClosedByInterruptException}.
     *
     * <p> If this thread is blocked in a {@link java.nio.channels.Selector}
     * then the thread's interrupt status will be set and it will return
     * immediately from the selection operation, possibly with a non-zero
     * value, just as if the selector's {@link
     * java.nio.channels.Selector#wakeup wakeup} method were invoked.
     *
     * <p> If none of the previous conditions hold then this thread's interrupt
     * status will be set. </p>
     *
     * <p> Interrupting a thread that is not alive need not have any effect.
     *
     * @throws  SecurityException
     *          if the current thread cannot modify this thread
     *
     * @revised 6.0
     * @spec JSR-51
     */
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

interrupt()的作用是中断本线程,本线程可以如中断自己,其他线程调动本线程的interrupt()方法
会通过checkaccess()检查权限,这个可能会有securityexception
如果本线程处于阻塞状态:调用wait()进如阻塞等待状态,或join()也会进入阻塞状态、sleep也会阻塞状态。如线程处于阻塞状态,再调用它的interrupt()方法,那么他的中断状态会被清理且会收到一个interruptedexcpetion异常。例如:线程通过wait()方法进入阻塞状态,此时通过interrupe()中断改该线程,调用interrupt()会立即将线程的中断标记为"true",但由于该线程处于阻塞状态,所有该中断标记立即被清除为false,同时会产生一个interruptexception异常。
如果线程被阻塞在一个selector选择器中,那么通过interrupt()中断它时,线程中断标记被设置为true,并且他会从选择操作中返回
如果不属于前面的操作,那么通过interrupte()中断线程是,它的中断比较设置为true

三、线程之间的通讯
jdk提供的线程通讯的api。
废弃的suspend/resume,容易死锁
在用的wait/notify、park/unpark
wait/notify 必须写同步代码块中,必须是统一对象锁的持有者线程调用。wait方法导致当前线程等待,加入该对象的等待集合中,并放弃当前持有的对象锁。 wait自动解锁,当时对顺序有要求,必须先wait,后notify

public void notifyWaitTest() throws InterruptedException {

       // wait和notify要在同步代码块里使用,基于对象的等待集合
        Thread thead = new Thread(()->{
            if(baozi==null){                                    //---while
                synchronized (this){
                    System.out.println("1、没有包子等待");
                    try {
                        this.wait();//代码执行这里,锁持有者,释放锁,并加如类对象的等待集合里面
                        System.out.println("正在取.......");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("2、买到包子");
        });
        thead.start();
        //3s后生产n包子
        Thread.sleep(3000);
        baozi=new Object();
        synchronized (this){
            this.notifyAll();//wait释放锁,生产这拿到锁,通知消费者,等生产执行结束才会释放锁,wait在拿到锁
            //notifyAll不会释放锁
            System.out.println("3、通知消费者 ");
            Thread.sleep(1000);
            System.out.println("测试等待1s后,测试看notify通知后会不会主动释放锁,还是执行代码块完成释放锁 ");
        }
    }

park和unpark不会释放锁,不能用synchronized,可以多次调用unpark,再调用park
park则等待许可,unpark制定提供线程提供许可。不会有顺序问题。

public void parkUnparkTest() throws InterruptedException {
        Thread thead = new Thread(( )->{
            if(baozi==null){                        //---while
                System.out.println("1、没有包子等待");
                LockSupport.park();
            }
            System.out.println("2、买到包子");
        });
        thead.start();
        Thread.sleep(3000);
        baozi=new Object();
        LockSupport.unpark(thead);
        System.out.println("3、通知消费者 ");
    }```

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值