资讯专栏INFORMATION COLUMN

并发编程基础知识一

muzhuyu / 1421人阅读

摘要:如果拿不到锁这个线程就会不断的尝试获得这把锁,直到拿到为止。关键字取得的锁都是对象锁而不是把一段代码方法当成锁。多个线程对象获得不同的锁他们互不影响。

线程安全:当多个线程访问某一个类时,这个类始终都能表现出正确的行为,那么这个类就是线程安全的

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

    private int count = 5;
    
    public synchronized void  run(){
        count--;
        System.err.println(this.currentThread().getName()+" count = "+count);
    }

当多个线程访问Thread的run()方法时,以排队的形式进行处理(排队是按照CPU分配的先后顺序而定的),一个线程要执行synchronized 修饰的方法里面的代码
1.尝试获得锁
2.如果拿到锁 执行synchronized 代码块内容 。如果拿不到锁 这个线程就会不断的尝试获得这把锁,直到拿到为止。而且多个线程同时去竞争这把锁,也就是会有锁竞争的问题

多个线程多个锁:每个线程都可以拿到自己的指定的锁,分别拿到锁以后执行synchronized方法体里面的内容。

synchronized关键字取得的锁都是对象锁 而不是把一段代码(方法)当成锁。
多个线程对象获得不同的锁 他们互不影响。

    private static int num;
    
    public static  synchronized void printNum(String tag){
        try{
            if(tag.equals("a")){
                num =100;
                System.err.println(" tag a");
                Thread.sleep(2000);
            } else{
                num = 200;
                System.err.println("tag b");
            }
        } catch(Exception e){
            e.printStackTrace();
        }
    }

在静态方法上面加synchronized 关键字 表示锁定class类,独占class类,类级别的锁

同步:synchronized 同步的概念就是共享。如果不共享资源,没有必须进行同步

异步:asynchronized 异步就是独立,相互之间不受制约。例如ajax请求

    public  synchronized void method1(){
        try {
            System.err.println(Thread.currentThread().getName());
            Thread.sleep(4000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void method2(){
        System.err.println(Thread.currentThread().getName());
    }

1.a线程先持有Object对象的lock锁 B线程如果在这个时候调用对象中的synchronized 方法则需要等待 ,也就是同步。
2.a线程先持有Object对象的lock锁 B线程可以以异步的方式调用线程对象中的非synchronized 修饰的方法 ,不需要等待。

同步的目的是为了线程安全,对于线程安全需要满足2个特性

原子性(同步)

可见性

脏读:在设计我们的程序的时候一定要考虑到问题的整体性,不然就会出现很经典的错误:脏读

    public synchronized void setValue(String  username, String password){
            this.username = username;
            this.password = password;
            
            try{
                Thread.sleep(2000);
            }catch(Exception e){
                e.printStackTrace();
            }
            System.err.println("setValue的最终结果是 username "+
            username+",password"+password);
        }
    
    public void getValue(){
        System.err.println("getValue方法得到username"+
username+",password"+password);
    }

在对一个对象的方法加锁时候,需要考虑到业务的整体性,即在setValue/getValue方法同时加上synchronized 关键字保证业务的原子性

锁重入:当一个线程获得一个对象的锁以后 ,再次请求此对象时可以再次获得对象的锁

    static class Main {
        public int i = 10;
        public synchronized void operationSup(){
            try {
                i--;
                System.out.println("Main print i = " + i);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    static class Sub extends Main {
        public synchronized void operationSub(){
            try {
                while(i > 0) {
                    i--;
                    System.out.println("Sub print i = " + i);
                    Thread.sleep(100);        
                    this.operationSup();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

当涉及到父类与子类相互执行的时候 可以使用锁重入

异常释放锁

    private int i = 0;
    
    public synchronized void operation(){
        while(true){
            try {
                i++;
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName() +
                 " , i = " + i);
                if(i == 20){
                    //Integer.parseInt("a");
                    throw new RuntimeException();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

如果一个业务分为多个子模块去执行。彼此之间相互独立,如果其中一个业务出现异常,采取的方式是记录日志,其他业务不受影响继续执行
如果一个业务分为多个子模块去执行,彼此之间是关联的,如果其中一个业务出现异常,采取的方式是抛出RuntimeException,及时终止业务。

减小锁的粒度:synchronized关键字来优化代码块的执行时间

    public void doLongTimeTask(){
        try {
            
            System.out.println("当前线程开始:" +

 Thread.currentThread().getName() + 
                    ", 正在执行一个较长时间的业务操作,其内容不需要同步");
            Thread.sleep(2000);
            
            synchronized(this){
                System.out.println("当前线程:" + 
                
                Thread.currentThread().getName() + 
                    ", 执行同步代码块,对其同步变量进行操作");
                Thread.sleep(1000);
            }
            System.out.println("当前线程结束:" +
 Thread.currentThread().getName() +
                    ", 执行完毕");
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

对任意的Object对象加锁

    public void method1(){
        synchronized (this) {    //对象锁
            try {
                System.out.println("do method1..");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public void method2(){        //类锁
        synchronized (ObjectLock.class) {
            try {
                System.out.println("do method2..");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    private Object lock = new Object();
    public void method3(){        //任何对象锁
        synchronized (lock) {
            try {
                System.out.println("do method3..");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

对字符串常量加锁 产生死循环

    public void method() {
        //new String("字符串常量")
        synchronized ("字符串常量") {
            try {
                while(true){
                    System.out.println("当前线程 : "  + 
                    Thread.currentThread().getName() + "开始");
                    Thread.sleep(1000);        
                    System.out.println("当前线程 : "  + 
                    Thread.currentThread().getName() + "结束");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

锁对象改变 如果锁所对象本身不发生改变,即使是属性改变,那么依然是同步的。 同一对象属性的修改不会影响锁的情况

    public synchronized void changeAttributte(String name, int age) {
        try {
            System.out.println("当前线程 : "  + 
Thread.currentThread().getName() + " 开始");
            this.setName(name);
            this.setAge(age);
            
            System.out.println("当前线程 : "  + 
Thread.currentThread().getName() + " 修改对象内容为: " 
                    + this.getName() + ", " + this.getAge());
            
            Thread.sleep(2000);
            System.out.println("当前线程 : "  + 
Thread.currentThread().getName() + " 结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

死锁问题:在设计程序时就应该避免双方相互持有对方的锁的情况

public void run() {
        if(tag.equals("a")){
            synchronized (lock1) {
                try {
                    System.out.println("当前线程 : "  + 
                    Thread.currentThread().getName() + " 进入lock1执行");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("当前线程 : "  + 
                    Thread.currentThread().getName() + " 进入lock2执行");
                }
            }
        }
        if(tag.equals("b")){
            synchronized (lock2) {
                try {
                    System.out.println("当前线程 : "  + 
                    Thread.currentThread().getName() + " 进入lock2执行");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("当前线程 : "  + 
                    Thread.currentThread().getName() + " 进入lock1执行");
                }
            }
        }
    }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70361.html

相关文章

  • 多线程编程完全指南

    摘要:在这个范围广大的并发技术领域当中多线程编程可以说是基础和核心,大多数抽象并发问题的构思与解决都是基于多线程模型来进行的。一般来说,多线程程序会面临三类问题正确性问题效率问题死锁问题。 多线程编程或者说范围更大的并发编程是一种非常复杂且容易出错的编程方式,但是我们为什么还要冒着风险艰辛地学习各种多线程编程技术、解决各种并发问题呢? 因为并发是整个分布式集群的基础,通过分布式集群不仅可以大...

    mengera88 评论0 收藏0
  • Java多线程学习(七)并发编程些问题

    摘要:相比与其他操作系统包括其他类系统有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少。因为多线程竞争锁时会引起上下文切换。减少线程的使用。很多编程语言中都有协程。所以如何避免死锁的产生,在我们使用并发编程时至关重要。 系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)syn...

    dingding199389 评论0 收藏0
  • 从小白程序员路晋升为大厂高级技术专家我看过哪些书籍?(建议收藏)

    摘要:大家好,我是冰河有句话叫做投资啥都不如投资自己的回报率高。马上就十一国庆假期了,给小伙伴们分享下,从小白程序员到大厂高级技术专家我看过哪些技术类书籍。 大家好,我是...

    sf_wangchong 评论0 收藏0
  • 我的阿里之路+Java面经考点

    摘要:我的是忙碌的一年,从年初备战实习春招,年三十都在死磕源码,三月份经历了阿里五次面试,四月顺利收到实习。因为我心理很清楚,我的目标是阿里。所以在收到阿里之后的那晚,我重新规划了接下来的学习计划,将我的短期目标更新成拿下阿里转正。 我的2017是忙碌的一年,从年初备战实习春招,年三十都在死磕JDK源码,三月份经历了阿里五次面试,四月顺利收到实习offer。然后五月怀着忐忑的心情开始了蚂蚁金...

    姘搁『 评论0 收藏0
  • Java多线程学习(七)并发编程些问题

    摘要:因为多线程竞争锁时会引起上下文切换。减少线程的使用。举个例子如果说服务器的带宽只有,某个资源的下载速度是,系统启动个线程下载该资源并不会导致下载速度编程,所以在并发编程时,需要考虑这些资源的限制。 最近私下做一项目,一bug几日未解决,总惶恐。一日顿悟,bug不可怕,怕的是项目不存在bug,与其惧怕,何不与其刚正面。 系列文章传送门: Java多线程学习(一)Java多线程入门 Jav...

    yimo 评论0 收藏0
  • Java并发编程笔记(

    摘要:并发编程实战水平很高,然而并不是本好书。一是多线程的控制,二是并发同步的管理。最后,使用和来关闭线程池,停止其中的线程。当线程调用或等阻塞时,对这个线程调用会使线程醒来,并受到,且线程的中断标记被设置。 《Java并发编程实战》水平很高,然而并不是本好书。组织混乱、长篇大论、难以消化,中文翻译也较死板。这里是一篇批评此书的帖子,很是贴切。俗话说:看到有这么多人骂你,我就放心了。 然而知...

    cnsworder 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<