zoukankan      html  css  js  c++  java
  • 13_线程安全

    【线程安全概念】

    当多个线程访问某一个类(或对象、方法)时,这个类始终都能表现出正确的行为,那么这个类就是线程安全的。

    【synchronized】

    可以在任意对象及方法上加锁,而加锁的这段代码被称为“互斥区”或“临界区”。

    【不加synchronized和加synchronized的不同情况】

    package com.higgin.part1;
    
    public class MyThread extends Thread{
        
        private int count=5;
        
        @Override
        public void run(){   //分别synchronized加锁和不加锁运行
            count--;
            System.out.println(this.currentThread().getName() + ", count="+count);
        }
        
        
        public static void main(String[] args) {
            /**
             * 分析:
             * 当多个线程访问myThread的run方法时,以排队的方式进行处理(这里的排队是指CPU分配的先后顺序而定的),
             * 一个线程想要执行synchronized修饰的方法里的代码:
             *     1.尝试获得锁
             *     2.如果拿到锁,执行synchronized代码 体的内容;拿不到锁,这个线程机会不断的尝试获得这把锁,直到拿到为止(这里有多个线程竞争这把锁)
             * 
             */
            MyThread mt = new MyThread();
            Thread t1 = new Thread(mt,"t1");
            Thread t2 = new Thread(mt,"t2");
            Thread t3 = new Thread(mt,"t3");
            Thread t4 = new Thread(mt,"t4");
            Thread t5 = new Thread(mt,"t5");
            
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
        }
    }

    【运行结果:不加synchronized锁(结果无法确定)】

    【运行结果:加synchronized锁】

    【多个线程多个锁的情况(区别对象锁和类锁)】

    package com.higgin.part2;
    
    public class MyThread {
        private static int num=0;
        
        /**
         * 分别用加static和不加static来运行
         * 不加static : 对象锁
         * 加上static : 类锁
         */
        public synchronized void printNum(String tag){
            try {
                if(tag.equals("a")){
                    num=100;
                    System.out.println("tag='a',set num OK !");
                    Thread.sleep(1000);
                }else if(tag.equals("b")){
                    num=200;
                    System.out.println("tag='b',set num OK !");
                }
                System.out.println("【finally】tag = "+tag +", num = "+num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        public static void main(String[] args) {
            //创建两个不同的对象
            final MyThread m1 = new MyThread();
            final MyThread m2 = new MyThread();
            
            Thread t1= new Thread(new Runnable() {
                @Override
                public void run() {
                    m1.printNum("a");
                }
            });
            
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    m2.printNum("b");
                }
            });
            
            t1.start();  //如果线程安全,必须得等t1执行完run方法,释放锁之后,t2才能获得锁执行run方法
            t2.start();
        }
    }

    【运行结果:不加static,非静态方法,为对象锁,可以看到t1和t2两个线程时并发执行的】

    【运行结果:加上static,静态方法,为类锁,t1和t2同步执行】

    【对象的同步和异步】

    同步:synchronized

      同步的概念就是共享,要牢记“共享”两个字,如果不是共享的资源,没有必要同步。

    异步:asynchronized

      异步的概念就是独立,相互之间不受到任何制约。比如http请求中,在页面发起Ajax请求时,我们依然可以继续浏览或者操作页面内容,二者之间没有任何关系。

    [ 线程安全的特性 ]

    同步的目的就是为了线程安全,线程安全的两个特性:原子性(同步)、可见性。

    package com.higgin.part3;
    
    /**
     * 对象锁的同步和异步问题
     */
    public class MyObject {
        private int num=0;
        
        public synchronized void setNum(int num){
            try {
                Thread.sleep(4000);
                this.num= num;
                System.out.println(Thread.currentThread().getName()+",set num = "+this.num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        /**
    * 针对的是同一个对象的对象锁 * 加上synchronized :当一个线程已经在执行setNum()方法的时候,另一个线程无法访问readNum方法 * 不加synchronized :当一个线程已经在执行setNum()方法的时候,另一个线程可以访问readNum方法
    */
    public void readNum(){ System.out.println(Thread.currentThread().getName()+", get Num = "+this.num); } public static void main(String[] args) { final MyObject mo = new MyObject(); //注意是一个对象!如果是两个对象,本身的锁已经不同 /** * t1线程先持有对象的锁,t2线程可以异步的方式调用对象中的非synchronized修饰的方法 * t1线程先持有对象的锁,t2线程如果在这个时候调用对象中的同步(synchronized)方法,则需等待t1释放锁,也就是同步执行 */ Thread t1 = new Thread(new Runnable() { @Override public void run() { mo.setNum(100); } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { mo.readNum(); } },"t2"); t1.start(); t2.start(); } }

    【运行结果:readNum()不加synchrobized锁,t1持有setNum方法的锁,但t2不会去等待t1释放锁,直接去执行readNum方法】

    【运行结果:readNum()加上synchronized锁,t1持有setNum方法的锁,t2必须等待t1释放锁才能获得readNum方法的锁】

    【分析】

    上面这个例子即一个“脏读”的例子。

    对一个对象的方法加锁时,需要考虑业务的完整性,即为setValue()/getValue()方法同时加锁synchronized关键字,保证业务的完整性。

    【问:当一个线程进入一个对象的synchronized的方法时,其他线程是否可以进入此对象的其他方法?】

    答:

    1.如果是创建的不同对象(如myObj1、myObj2对象),那么两个对象执行各自的方法不会相互影响,为异步的。

    2.如果是同一个对象(只有一个myObj对象),如果一个线程进入一个对象的synchronized修饰的方法,如果其他的那个方法是非sychronized方法,那么是可以访问的。

    3.如果是同一个对象(只有一个myObj对象),如果一个线程进入一个对象的synchronized修饰的方法,如果其他的那个方法是sychronized方法,那么是不可以访问的。

  • 相关阅读:
    CF_402C Searching for Graph 乱搞题
    zoj Simple Equation 数论
    zoj 3757 Alice and Bob and Cue Sports 模拟
    uva_12535
    boj1267 Infinite’s Cave 树形dp + 背包
    CF_216_Div_2
    nxlog4go 简介
    log4go的一些改进设想
    nxlog4go 的配置驱动
    nxlog4go Log Levels and Pattern Layout
  • 原文地址:https://www.cnblogs.com/HigginCui/p/6671314.html
Copyright © 2011-2022 走看看