zoukankan      html  css  js  c++  java
  • 线程

    • 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
    • 实现一个线程:
    extends Thread(重写run方法)

    implements Runnable
    • synchronized:可以在任意对象方法上加锁,而加锁的这段代码称为互斥区,或临界区。
    • 注:一个线程想要执行syschronized修饰的方法里的代码,首先是尝试获得锁,如果拿到锁执行syschronzied代码体内容,拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,而且是多个线程同时竞争这把锁。(也就是会有锁竞争的问题)。
     1 public class MyThread extends Thread{
     2 
     3     private int count=5;
     4 
     5     public synchronized void run() {
     6         count--;
     7         System.out.println(this.currentThread().getName()+":"+count);
     8     }
     9     
    10     public static void main(String args[]) {
    11         
    12         MyThread thread=new MyThread();//当前线程
    13         Thread t1=new Thread(thread,"1号线程");
    14         Thread t2=new Thread(thread,"2号线程");
    15         Thread t3=new Thread(thread,"3号线程");
    16         Thread t4=new Thread(thread,"4号线程");
    17         Thread t5=new Thread(thread,"5号线程");
    18         t1.start();
    19         t2.start();
    20         t3.start();
    21         t4.start();
    22         t5.start();
    23         //按照线程启动,结果应该为:43210,但是run方法不加synchronized达不到预期结果
           //使用
    synchronized可以锁住但是存在锁竞争问题
    24     }
    25 }
    • 多个线程多个锁
    • 注:关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当作多个对象通过多个线程访问synchronized修饰的方法时,线程拿到的是自己指定对象的锁(不是相同的锁),这种情况下会导致结果达不到预期,要想多线程不同对象拿到相同的锁,可以将synchronzied修饰的方法改为静态的(static),表示锁定.class类,类一级别的锁(独占.class类)
    public class MultiThread {
    
        private static int num=0;
        
        //static
        public static synchronized void printNum(String tag) {
            try {
                if(tag.equals("a")) {
                    num=100;
                    System.out.println("a");
                    Thread.sleep(2000);
                }else {
                    num=200;
                    System.out.println("b");
                }
                System.out.println(tag+":"+num);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        public static void main(String args[]) {
            MultiThread multiThread1=new MultiThread();        
            MultiThread multiThread2=new MultiThread();
            //线程1
            Thread t1=new Thread(new Runnable() {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    multiThread1.printNum("a");
                }
            });
            //线程2
            Thread t2=new Thread(new Runnable() {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    multiThread2.printNum("b");
                }
            });
            t1.start();
            t2.start();    
            //预期结果应该是:a,a:100,b,b:200;但是即使方法加上synchronzied修饰,结果也达不到预期
            //2个线程分别得到的是multiThread1与multiThread2的对象锁,达不到同步的效果
            //要想达到效果可以将方法改成静态的,此时属于类(MultiThread)一级别的锁
            //2个线程要做到不同时调用printNum方法,锁.class类  
        }
    }
    • 同步与异步
    1. 同步:synchronzied同步的概念就是共享,不是共享资源没必要同步同步的目的就是为了线程安全,线程安全条件:原子性(同步)可见性。
    2. 异步:asynchronized异步的概念就是独立,互相之间不受任何制约(类似Ajax)。
    • 脏读
    1.当数据库的一个事务A正在使用一个数据但还没有提交,另外一个事务B也访问到了这个数据,还使用了这个数据,这就会导致事务B使用了事务A没有提交之前的数据。(如果当前事务A回滚,那么事务B拿到的就是脏数据)
    
    2.线程A去查某一数据,在还没有拿到该数据的时候,线程B修改了该数据,线程A拿到的是B没修改之前的数据,这是由于数据库一致性读的特性造成的。(在数据库删改的过程中,会将数据存入快照中,如果监听到数据有改动会去快照中找旧数据,所以拿到的是之前的数据)
    • synchronized锁重入:使用synchronized时,当一个线程得到了一个对象的锁后,再次请求此对象时可以再次得到该对象的锁。
    /**
     * synchronized锁重入
     */
    public class SyncDubbo1 {
    
        public synchronized void method1() {
            System.out.println("1");
            method2();
        }
        public synchronized void method2() {
            System.out.println("2");
            method3();
        }
        public synchronized void method3() {
            System.out.println("3");
        }
        
        public static void main(String args[]) {
            SyncDubbo1 dubbo=new SyncDubbo1();
            //线程1
            Thread t1=new Thread(new Runnable() {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    dubbo.method1();
                }
            });        
            t1.start();
        }
    }
    • 父子继承关系中要想实现同步,都得使用synchronized,否则存在线程安全问题。
    /**
     * 存在继承关系时,synchronzied修饰的父类子类方法,可以实现线程安全
     */
    public class SyncDubbo2 {
        
        static class A{
            public int i=10;
            
            public synchronized void add(){
                try {
                    i--;
                    System.out.println("A:"+i);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        static class B extends A{
            
            public synchronized void add(){
                try {
                    while(i>0){
                        i--;
                        System.out.println("B:"+i);
                        Thread.sleep(1000);
                        super.add();
                    }
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        public static void main(String args[]){
            //线程1
            Thread t1=new Thread(new Runnable() {                    
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    B b=new B();
                    b.add();
                }
            });        
            t1.start();
        }
    }
    • synchronized使用String的常量加锁,会出现死循环问题。
    • volatile关键字volatile关键字主要作用是使变量多个线程之间可见。
    1. volatile的作用就是强制线程到主内存(共享内存)里去读取变量,而不去线程工作内存区里去读取,从而实现多个线程间的变量可见,也就是满足线程安全的可见性。
    2. volatile不具备同步性(原子性),可以算是一个轻量级的synchronized,性能要比synchronzied强大很多,不会造成阻塞(netty底层大量使用volatile)。
    3. volatile用于多个线程之间变量可见,不能代替synchronzied的同步功能。(AtomicInteger类可以实现原子性,但是它只保证本身方法的原子性,不能保证多个方法的原子性)。
    • 线程通讯
    1. while轮询的方式(通过条件判断是否达到实现通讯,用volatile)。
    2. 同步synchronized:线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通讯就成为整体的必要方法之一
    3. 使用wait/notify:waitnotify必须配合synchronized关键字使用。wait方法释放锁,notify方法不释放锁

     

  • 相关阅读:
    hbase 学习笔记一---基本概念
    hdu 4496 (并差集)
    rqnoj-105-核电站问题-dp
    面试之BI-SQL--table转换
    Android:从程序员到架构师之路Ⅲ_高焕堂
    Linux 的进程组、会话、守护进程
    Oracle创建dblink报错:ORA-01017、ORA-02063解决
    JSP 指令
    JSP 生命周期
    JSP 结构
  • 原文地址:https://www.cnblogs.com/LJing21/p/10632037.html
Copyright © 2011-2022 走看看