java基础之线程 认识一下synchronize

把java基础撸一边,从简单的开始。java

线程部分:缓存

存在都是有理由的。若是没有线程咱们的程序只能是下图这个样子,想象一个下假若有1千万个请求,每一个请求1秒,这得请求多长时间
bash


若是不止一个窗口处理事情,这个时候,线程的优势就体现出来了,这样执行完这么多请求就除4了多线程


有点是会体现出来,但同时也暴露出了多线程的不足。一个CPU只能运行一个线程。四核也就是能够运行4个线程,平时看这开100个线程也没事,一下就执行完了,可是这些CPU运行的时间片断过短,执行快因此看上去也没事,可是若是超过了必定范畴也会出问题,(这不是本章的重点),还有就是数据容易成为脏数据,若是多个线程去修改同一个int,那么这个字符串最终是什么样呢?还有各类抢占资源,死锁...等等。jvm

本章重点说的是脏数据问题。其余的后续更新ide

实例,多线程下脏数据的出现:spa

public class Demo21 extends Thread{

    private int va = 1;
    
    @Override
    public void run() {
        for (int i = 0 ; i < 100 ; i++){
            va ++ ;
            System.out.println("Thread Name :"+Thread.currentThread().getName() + "va :" +va);
        }
    }

    public static void main(String[] age){
        Demo21 demo21 = new Demo21();
        demo21.start();
        Demo21 demo22 = new Demo21();
        demo22.start();
        Demo21 demo33 = new Demo21();
        demo33.start();
    }

}复制代码

三个线程开启,对va进行自加1。按照理想状态,是1 2 3 ...300操作系统

可是结果是什么样呢?.net

Thread Name :Thread-0va :2
Thread Name :Thread-2va :2
Thread Name :Thread-1va :2
Thread Name :Thread-2va :3
Thread Name :Thread-0va :3
复制代码

三个线程 同时打印2。明明va++很短,执行很快为何仍是会出现这个状况。线程


简单看一下jvm的运行空间是什么回事,这里看两个区域,线程共享区,线程独占区。常量是会放到线程共享区的,也就是说没个线程均可以拿到这个值,而线程独占区,里面的数据只能够被当前线程享用(线程之间的数据通讯另说)。这样就能够了解到为何会出现这个状况。

1:数据不是能够立刻写的,从地中中读到数据进入CPU缓存,CPU再作修改,修改完以后在给到主存中

2:A线程修改数据B线程并不知道。

线程一个危险就是这个脏数据,破坏了数据的一致性 。解决这些方法java中提供了不少操做

synchronize,AtomicIntege,LongAdder

此次主要介绍synchronize

public class Demo21 implements Runnable{

    private int va = 1;

    public void get()  {
        synchronized (Demo21.class){
            va++;
            System.out.println("Thread Name :"+Thread.currentThread().getName() + "va :" +va);
        }
    }

    @Override
    public void run() {
        for (int i = 0 ; i < 100 ; i++){
            get();
        }
    }

    public static void main(String[] age){
        Demo21 demo21 = new Demo21();
        Thread thread = new Thread(demo21);
        Thread thread1 = new Thread(demo21);
        Thread thread2 = new Thread(demo21);
        thread.start();
        thread1.start();
        thread2.start();
    }

}复制代码

打印结果

Thread Name :Thread-0va :2
Thread Name :Thread-0va :3
Thread Name :Thread-0va :4
Thread Name :Thread-0va :5
Thread Name :Thread-1va :6
Thread Name :Thread-0va :7
Thread Name :Thread-0va :8
Thread Name :Thread-0va :9
Thread Name :Thread-0va :10
复制代码

能够见到加了这个关键字就能够顺序执行。

参考一下java synchronize原理总结,对synchronize作一个初步了解

synchronize的底层是使用操做系统的mutex lock实现

内存可见性:一个线程对共享变量值的修改,可以及时被其余线程看到。

操做原子性:持有同一个锁的两个同步快只能串行进入


synchronize保证 1,2,3,4线程执行synchronize修饰包括的程序代码块中,是保证进入的只有一个线程。synchronize能够理解为一个只容许一个线程进入的大门,进来一个就用它持有的锁给锁住,防止其余线程进入,当代码块执行完毕以后,再开启。

java是面向对象的语言,synchronize用的锁也是存在java对象头里。

synchronized有三种使用方式:

1:修饰实例方法

2:修饰静态方法

3:修饰代码块


修饰实例方法:

public class A {

    public void A(){
        System.out.println("A");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void B(){
        System.out.println("B");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}复制代码

public class Demo21 {

    public static void main(String[] age){
        A a = new A();
        new Thread(new Runnable() {
            @Override
            public void run() {
                long l = System.currentTimeMillis();
                a.A();
                long l2 = System.currentTimeMillis() - l;
                System.out.println(l2);
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                long l = System.currentTimeMillis();
                a.B();
                long l2 = System.currentTimeMillis() - l;
                System.out.println(l2);
            }
        }).start();
    }

}复制代码

若是没有synchronize修饰

A
B
1001
1001
复制代码

是没有串行执行的

public synchronized void A()
public synchronized void B()复制代码

修饰实例方法以后

A
1001
B
2000
复制代码

能够看到是串行执行,有锁的效果

public class Demo21 {

    public static void main(String[] age){
        A a = new A();
        A a1 = new A();
        new Thread(new Runnable() {
            @Override
            public void run() {
                long l = System.currentTimeMillis();
                a.A();
                long l2 = System.currentTimeMillis() - l;
                System.out.println(l2);
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                long l = System.currentTimeMillis();
                a1.B();
                long l2 = System.currentTimeMillis() - l;
                System.out.println(l2);
            }
        }).start();
    }

}复制代码

若是是不一样的实例执行A B方法

A
B
1001
1001
复制代码

这样就能够知道,修饰非静态实例方法的锁,就是它的实例对象

修饰静态方法:

public static synchronized void B()
public static synchronized void A()

复制代码

修饰静态方法以后

A
B
1000
2000
复制代码

建立两个实例,也是串行执行,这样能够证实是,当前类加锁,进入同步代码块钱要获取当前类对象的锁

修饰代码块

public void A(){
    synchronized (A.class){
        System.out.println("A");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

public void B(){
    synchronized (A.class) {
        System.out.println("B");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}复制代码

代码执行后

A
B
1001
2000
复制代码

在synchronize(对象.class)这个对象就是这个同步方法的锁了

这里就是对象synchronize作了一下简单的认识,其实还有不少复杂的东西,要了解synchronize还须要知道jvm,操做系统等等。但能力不足,就介绍到这里。

推荐三篇文章:

synchronize三中用法

java synchronize原理总结

内存可见性

相关文章
相关标签/搜索