zoukankan      html  css  js  c++  java
  • 多线程简单实例(1)真的需要synchronized么?

    说道多线程的安全问题,很多人想到就就是加锁。用到synchronized关键字。

    那就要先说说synchronized问什么能够保证线程安全了。

    首先要了解线程的工作方式:线程工作分为工作内存和主内存。主内存就是堆和静态区。当线程运行时,首先将主内存的数据拿到工作内存

    然后在工作内存中运行,再将数据写回主内存。工作内存是私有的,但是主内存却是共享的。

    那么线程不安全的主要根源就是不能线程读写主内存的共享数据。

    那么判断要不要加锁,在什么位置加锁就有了依据——共享数据

    下面看一个例子:

    package code.thread;
    
    public class SynchronizedDome extends Thread{
        int a = 0;
        Object obj = new Object();
        @Override
        public void run() {
            synchronized(obj) {
                for(int i=5;i>0;i--){
                    System.out.println(a);
                    a++;
                    try {
                        Thread.sleep(0);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
        
        public static void main(String[] args) {
            SynchronizedDome dome = new SynchronizedDome();
            SynchronizedDome dome2 = new SynchronizedDome();
            SynchronizedDome dome3 = new SynchronizedDome();
            System.out.println("Thread start:");
            dome.start();
            dome2.start();
            dome3.start();
        }
    }

    执行结果:

    Thread start:
    0
    1
    0
    0
    2
    3
    1
    1
    2
    3
    4
    4
    2
    3
    4

    看到没,并没有发生错乱,与预想的输出结果一致

    那么你可能会说,这是synchronized的功劳。真的是这样的么,稍微改动过一下在看看

    package code.thread;
    
    public class SynchronizedDome extends Thread{
        int a = 0;
        Object obj = new Object();
        @Override
        public void run() {
            //synchronized(obj) {
            {
                for(int i=5;i>0;i--){
                    System.out.println(a);
                    a++;
                    try {
                        Thread.sleep(0);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
        
        public static void main(String[] args) {
            SynchronizedDome dome = new SynchronizedDome();
            SynchronizedDome dome2 = new SynchronizedDome();
            SynchronizedDome dome3 = new SynchronizedDome();
            System.out.println("Thread start:");
            dome.start();
            dome2.start();
            dome3.start();
        }
    }

    看到没,还是安全的,并没有因为没加锁而发生错乱

    那么我没就要分析一下了,根据我们上面所说的,线程的不安全是因为数据的共享

    这个例子中,分别new了三个线程对象。

    每个对象在栈上有一个变量a,他们分别属于不同的对象。所以三个线程操作的都是属于自己本身类的数据。是对象私有的。

    所以不存在数据的共享,那么就不用加锁了。

    我没在看一个例子,让他们数据共享,将变量定义为静态变量

    package code.thread;
    
    public class SynchronizedDome2 {
        public static void main(String[] args) {
            Dome dome = new Dome();
            Thread thread = new Thread(dome);
            Thread thread2 = new Thread(dome);
            
            thread.start();
            thread2.start();
            
        }
    }
    
    class Dome implements Runnable {
        static int a = 0;
        @Override
        public void run() {
            //synchronized(this){
            {
                for(int i=0;i<5;i++) {
                    System.out.println(Thread.currentThread().getName()+":  "+a++);
                }
            }
        }
    }

    输出结果:

    Thread-1: 0
    Thread-0: 1
    Thread-1: 2
    Thread-0: 3
    Thread-1: 4
    Thread-0: 5
    Thread-1: 6
    Thread-1: 8
    Thread-0: 7
    Thread-0: 9

    这么换乱,而且我们侥幸没有得到错误的结果。如果多运行几次就会看到可能会出现错误的结果

    那么下面用锁来解决,看看有什么不同。

    程序就是将什么代码加锁注释去掉。

    输出结果:

    Thread-0: 0
    Thread-0: 1
    Thread-0: 2
    Thread-0: 3
    Thread-0: 4
    Thread-1: 5
    Thread-1: 6
    Thread-1: 7
    Thread-1: 8
    Thread-1: 9

    总结:总的来说线程不安全是由于共享数据的读写不同步引起的。当不涉及到共享数据,也就无不安全可说了。

    synchronized关键字保证了操作的原子性和可见性。原子性就是说,一个执行步奏完整的执行完毕,不会再执行的过程中被其他线程打断。

    可见性是说,当执行完锁定的代码块后,在解锁之前会把最新的数据写入到主内存中。并且清空其他线程工作内存中该数据的值。保证了该数据时最新的。

  • 相关阅读:
    javascript实现网页分享至朋友圈功能
    Vue中全局过滤器期与局部过滤器期的使用
    vue中添加与删除,关键字搜索
    vue生命周期
    4种常用的Ajax请求方式
    CSS 基础总结
    BEM思想之彻底弄清BEM语法
    学习前端过程中用到的资料
    深入理解,函数声明、函数表达式、匿名函数、立即执行函数、window.onload的区别.
    html基础
  • 原文地址:https://www.cnblogs.com/justenjoy/p/7044656.html
Copyright © 2011-2022 走看看