Java多线程~谈谈自己对于synchronized、volatile关键字的理解,线程的状态转换以及创建线程的方式

目录

创建线程的方式

继承Thread类

实现Runnable接口 

实现Callable接口

线程的状态转换

volatile关键字

synchronized关键字

synchronized关键字的特性

synchronized原理


创建线程的方式

在java中,创建线程就是new一个Thread,有三种方式,第一种是继承Thread类、第二种是实现Runnable接口、第三种是实现Callable接口

继承Thread类

继承Thread类,重写run方法(run方法即定义要执行的任务代码)

public class TestDemo5 {
    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("继承Thread类方式创建线程");
        }
    }
    public static void main(String[] args) {
        TestDemo5.MyThread thread = new TestDemo5().new MyThread();
        thread.start();
    }
}

实现Runnable接口 

首先实现Runnable接口,再重写run方法,之后创建线程时传入创建的runnable对象

public class TestDemo5 {
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("实现Runnable接口方式创建线程");
        }
    }

    public static void main(String[] args) {
        TestDemo5.MyRunnable myRunnable = new TestDemo5().new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

实现Callable接口

public class TestDemo6 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 0; i <= 1000; i++) {
                    sum += i;
                }
                return sum;
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);
        t.start();
        int ret = futureTask.get();
        System.out.println(ret);
    }
}

线程的状态转换

线程的状态有六种:NEW(初始状态)、RUNNABLE(可运行态)、WAITING(等待状态)、TIMED_WAITING(超时等待状态)、BLOCKED(阻塞状态)、TERMINATED(终止状态) 

创建一个线程,线程就进入了NEW状态,当调用start方法时,进入RUNNABLE状态,如果调用了wait、join等方法时就进入WAITING、TIMED_WAITING状态,当多个线程竞争同一把锁时,竞争失败的线程进入BLOCKING状态,线程执行完成后,进入TERMINATED状态。

volatile关键字

使用volatile关键字是为了保证线程安全的,当某个线程对共享变量只是读操作而没有写操作时,使用volatile就可以保证线程安全。volatile的作用是保证可见性和有序性,因为读操作本来就具有原子性,因此仅使用volatile就可以保证线程安全。

· volatile能保证内存的可见性

使多个线程对同一个共享变量操作具有可见性

代码在写入volatile修饰的变量时,先改变工作内存中volatile变量的副本值,再将改变后的副本的值从工作内存刷新到主内存。

代码在读取volatile修饰的变量时,先从主内存中读取volatile变量的最新值到线程的工作内存中,再从工作内存中读取volatile变量的副本。

· volatile可以禁止代码指令重排序,建立内存屏障

例如:小李对自己的安排是:

1、打篮球;

2、吃饭;

3、取快递

如果不加volatile,小李的执行顺序可能是2、1、3或3、2、1等等,但是加上volatile就是1、2、3,这就是禁止指令重排序的作用。

· 但是volatile不能保证原子性

synchronized关键字

多个线程使用同一个对象进行加锁(基于对象头加锁),可以实现线程间的同步互斥。简单地说,它的原理就是基于对象头加锁,满足原子性、可见性、有序性

synchronized关键字的特性

①互斥:

synchronized会起到互斥效果,某个线程执行到某个对象的synchronized中时,如果其他线程也执行到同一个的对象的synchronized就会阻塞等待,进入synchronized代码块相当于加锁,退出synchronized修饰的代码块相当于释放锁。synchronized的锁是存在Java对象头中的。

互斥可以满足原子性,某个线程执行同一个对象加锁的同步代码,排斥另一个线程加锁,满足最小执行单位。

②刷新内存:

synchronized结束释放锁,会把工作内存中的数据刷新到主存,其他线程申请锁,获取的始终是最新数据(满足可见性)。synchronized的工作过程:获得互斥锁->从主内存中拷贝变量的最新副本到工作内存->执行代码->将更改后的共享变量的值刷新到主内存->释放互斥锁

③有序性:

某个线程执行一段同步代码,不管如何重排序,期间都不可能有其他线程指令的执行,这样多个线程执行同步代码,就是满足一定的顺序的

④可重入:

同一个线程可以多次申请同一个对象锁

synchronized原理

结合锁策略,谈synchronized锁的基本特点:

①开始时是乐观锁,如果锁冲突频繁,就转换为悲观锁

②开始时是轻量级锁实现,如果锁被持有的时间较长,就会转为重量级锁

③实现轻量级锁时大概率用到自旋锁策略

④是一种不公平锁

⑤是一种可重入锁

⑥不是读写锁

加锁工作过程:

JVM将synchronized锁分为无锁、偏向锁、轻量级锁、重量级锁状态,会根据情况依次进行升级。

①偏向锁:第一个加锁的线程,优先进入偏向锁状态,偏向锁并不是真的“加锁”,只是给对象头做了一个“偏向锁的标记”,记录这个锁属于哪个线程。如果后续没有其他线程来竞争该锁,就不用进行其他同步操作了,如果后续有其他线程竞争该锁,就取消原来的偏向锁状态,进入一般的轻量级锁。

②轻量级锁:随着其他线程进入竞争,偏向锁的状态被消除,进入轻量级锁状态(自适应的自旋锁)。这里的轻量级锁是通过CAS实现的,通过CAS检查并更新一块内存,如果更新成功表示加锁成功,如果更新失败,则认为锁被占用,继续自旋式的等待(并不放弃CPU).自旋操作是一直让CPU进行空转,比较浪费CPU资源。

③重量级锁:当线程冲突进一步激烈,自旋锁不能快速获取到锁状态,就会升级为重量级锁,重量级锁基于系统级别的mutex lock实现,代价非常大,是系统加锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li_yizYa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值