美文网首页
高并发(5)- 多线程之间的共享

高并发(5)- 多线程之间的共享

作者: 残冬十九 | 来源:发表于2020-03-27 16:53 被阅读0次

前言

上篇文章讲解了多线程的运行状态。本篇文章就来讲讲线程之间的共享。


一、为什么要线程共享

因为线程都是独立的,相互之间是不可见的,所以当两个线程对一个数据进行操作时,就很容易出现问题。

/**
 * @version 1.0
 * @Description 不同步线程demo
 * @Author wb.yang
 */
public class NoSyncDemo {

    static Integer count = 0;

    public static class CountThread extends Thread {
        @Override
        public void run() {
            count++;
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new CountThread().start();
        }
        System.out.println(count);
    }
}

image.gif
        从代码中看出,我们循环了1000次相加count关键字,结果应该是1000,我们看下结果
image image.gif

   可见,结果才输出了994,明显跟预期的结果不同。导致这个问题的就是线程的共享,因为线程之间是不不见的,可能线程A拿了count = 100,这时候还没有进行count++的操作,这时候线程B抢到了cpu,也取出了count=100这个时候,就已经数据不同步了。造成了线程安全问题。

针对线程安全问题,我们就要进行线程之间的数据共享。

二、线程共享方式

1.volatile

volatile关键字,最轻量的同步机制​。使用volatile关键字,可以使变量成为可见性。保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。

/**
 * @version 1.0
 * @Description volatile线程demo
 * @Author wb.yang
 */
public class VolatileSyncDemo {
    private volatile static boolean ready;
    private static int number;

    private static class CountThread extends Thread {
        @Override
        public void run() {
            System.out.println("计数线程启动.......");
            //无限循环
            while (!ready) ;
            System.out.println("number = " + number);
        }
    }

    public static void main(String[] args) {
        new CountThread().start();
        SleepTools.second(1);
        number = 51;
        ready = true;
        SleepTools.second(5);
        System.out.println("main is ended!");
    }
}

image.gif
               从代码中可以看出,我们先启动了线程,之间打印了number,然后在主线程中赋值number=51,如果按照线程之间数据不可见的话,那么应该打印null。我们看下结果把。
image image.gif

          我们可以看出,输出的赋值后的数据,这事为什么呢?这个就涉及到了volatile关键字,通过volatile关键字,使变量在线程之间可见,所以可以知道number的变化。

2.synchronized

synchronized内置锁​,有以下几种锁法

  • 普通同步方法,锁是当前对象实例
  • 静态同步方法,锁是当前Class对象
  • 同步代码块,锁是括号中的参数对象

当一个线程访问同步代码块时,必须先获得锁才能执行同步代码块中的代码,当退出或者抛出异常时,必须要释放锁。

/**
 * @version 1.0
 * @Description TODO
 * @Date 2020/3/25 17:41
 */
public class SynchronizedSyncDemo {

    static Integer count = 0;
    private static Object object = new Object();

    public static class CountThread extends Thread {
        @Override
        public void run() {
            synchronized (object){
                count++;
            }
        }

    }

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new CountThread().start();
        }
        SleepTools.second(2);
        System.out.println(count);
    }
}

image.gif
     从代码可以看出,我们在run方法中添加了synchronized,并且锁了方法,我们看下结果
image image.gif

    我们看到结果是正常的。之所以正常,是因为用了synchronized,来将count++给加锁,进行锁住了,当一个线程加锁锁住这些代码的时候,其他的线程过来必须等待上一个线程执行完代码释放锁,否则不会持有这把锁,只能在这里等待,保证了线程的共享。

3.ThreadLocal

ThreadLocal是在每个线程都使用一个副本,线程之间隔离,操作的是副本中变量的值,只在这个线程中生效,其他线程中的这个变量,又是新的本地副本,所以不会变化

/**
 * @version 1.0
 * @Description ThreadLocalDemo
 * @Author wb.yang
 */
public class ThreadLocalSyncDemo {

    static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

    public static class CountThread extends Thread {

        int id;

        public CountThread(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":start");
            Integer s = threadLocal.get();
            s = s + id;
            threadLocal.set(s);
            System.out.println(Thread.currentThread().getName() + ":"
                    + threadLocal.get());
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new CountThread(i).start();
        }
    }
}

image.gif
      这个是一个ThreadLocal变量,默认值是1,然后启动了三个线程,进行操作。
image image.gif

从结果中看出,每个线程都是单独的处理,县城之间并没有干扰,这个就是ThreadLocal的作用,来保证线程的共享。

总结

线程之间难免要线程共享来保证线程安全,所以就产生了以上几种方法来保证线程同步安全。

相关文章

  • 高并发(5)- 多线程之间的共享

    前言 上篇文章讲解了多线程的运行状态。本篇文章就来讲讲线程之间的共享。 一、为什么要线程共享 因为线程都是独立的,...

  • 一些基础之一

    高并发 面试的时候都喜欢问这个问题,解决高并发的问题根源在于解决高并发下共享资源的控制问题。也就牵扯到多线程下共享...

  • Java 多线程(四):ThreadLocal 详解

    ThreadLocal 多线程在并发执行时,需要数据共享,因此才有了 volatile 变量解决多线程数据之间可见...

  • ThreadLocal解析

    原理 产生线程安全问题的根源在于多线程之间的数据共享。如果没有数据共享,就没有多线程并发安全问题。ThreadLo...

  • ThreadLocal解析

    原理 产生线程安全问题的根源在于多线程之间的数据共享。如果没有数据共享,就没有多线程并发安全问题。ThreadLo...

  • 2019-08-16 Synchronized的使用

    为什么要使用Synchronized关键字?为了解决线程高并发安全问题,共享数据,多线程共同操作共享数据,Sync...

  • java多线程高并发

    “高并发和多线程”总是被一起提起,给人感觉两者好像相等,实则高并发 ≠ 多线程 多线程是完成任务的一种方法,高并发...

  • 什么是高并发与多线程?

    高并发和多线程”总是被一起提起,给人感觉两者好像相等,实则高并发 ≠ 多线程 多线程是完成任务的一种方法,高并发是...

  • 高并发Java

    高并发Java(1):前言 高并发Java(2):多线程基础 高并发Java(3):Java内存模型和线程安全 高...

  • 从一段代码说起锁和事务

    锁 锁是为了解决高并发产生的多线程对共享资源进行并发访问时,由于后端接口『来不及』处理线程请求的数据,导致最终出现...

网友评论

      本文标题:高并发(5)- 多线程之间的共享

      本文链接:https://www.haomeiwen.com/subject/dxqruhtx.html