zoukankan      html  css  js  c++  java
  • 线程同步

    一.简介

      在一般情况下,创建一个线程是不能提高程序的执行效率的,所以要创建多个线程。但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就要使线程同步。

    线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,目前实现线程同步的方法有很多,临界区对象就是其中一种。

    二.线程同步的方式和机制  

      临界区、互斥量、事件、信号量四种方式
      临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event)的区别:
        1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源      进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进      入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。
        2、互斥量:采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不      会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。
        3、信号量:它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。
        4、事 件: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作。
     
    三.synchronized
      图例说明:因为线程t1和t2共用同一个对象timer,因此当两个线程同时访问的时候,就会因为不同线程对num的操作,造成不同步。
      
      示例代码:
      
    public class TestSync implements Runnable{
    
        Timer timer = new Timer();
        public static void main(String[] args) {
            TestSync test = new TestSync();
            Thread t1 = new Thread(test);
            Thread t2 = new Thread(test);
            t1.setName("线程1");
            t2.setName("线程2");
            t1.start();
            t2.start();
    
        }
    
        public void run() {
            timer.add(Thread.currentThread().getName());
        }
    
    }
    
    class Timer {
        private static int num = 0;
        
        public void add(String name){
        //public synchronized void add(String name){   //①表示执行当前方法过程中,锁定当前对象,是当前对象被锁定了
            //synchronized(this){  //②表示锁定某一块东西
                num++;
                try{
                    Thread.sleep(1);
                }catch(InterruptedException e){
                }
                System.out.println(name+",你是第"+num+"个使用Timer的线程");
            //}
        }
        
    }
    View Code

      此时结果是:

    线程1,你是第2个使用Timer的线程
    线程2,你是第2个使用Timer的线程

      此时,可以用关键字synchronized通过线程的互斥来实现同步,

      (提供了两种方法:①方法前加上synchronized;②synchronized(this){}):

      放开注释的其中之一,此时结果是:

    线程1,你是第1个使用Timer的线程
    线程2,你是第2个使用Timer的线程

    四.死锁  

      所谓死锁: 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
      所以使用了synchronized关键字后,当几个线程共同竞争资源的时候就会产生死锁。如下例:
      
    public class TestDeadLock implements Runnable{
    
        public int flag = 0;
        static Object obj1 = new Object();
        static Object obj2 = new Object();
        
        public void setFlag(int flag){
            this.flag = flag;
        }
        
        public static void main(String[] args) {
            TestDeadLock tdl1 = new TestDeadLock();
            TestDeadLock tdl2 = new TestDeadLock();
            tdl1.setFlag(0);
            tdl1.setFlag(1);
            Thread thread1 = new Thread(tdl1);
            Thread thread2 = new Thread(tdl2);
            thread1.start();
            thread2.start();
        }
    
        public void run() {
            System.out.println("flag="+flag);
            if(flag == 1){
                synchronized(obj1){   //先申请资源1
                    try{
                        Thread.sleep(500);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    
                    synchronized(obj2){  //后申请资源2
                        System.out.println("情况"+flag+"的资源申请完毕");
                    }
                }
            }else if(flag ==0){
                synchronized(obj2){  //先申请资源2
                    try{
                        Thread.sleep(500);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    
                    synchronized(obj1){   //后申请资源1
                        System.out.println("情况"+flag+"的资源申请完毕");
                    }
                }
            }
        }
    
    }
    View Code

    五.synchronized作用域 

      synchronized只会对使用了synchronized关键字的对象加锁,这样当被加锁对象正在访问时,另外的线程要对其进行访问才会互斥,未被加锁的对象不会产生互斥,即使都会用到同样的资源。因此要实现对某资源的同步就必须要把用到此资源的对象都加上synchronized关键字。如下例:

    public class TestLockConcept implements Runnable{
    
        int b = 100;     //比如这是多处会用到的资源  
        
        public synchronized void m1() throws InterruptedException {
            b = 1000;
            Thread.sleep(2000);
            System.out.println("m1:b = " + b);
        }
        
    //    public void m2() {
    //        System.out.println("m2:b = " + b);    //此时m2未加锁,m1加了锁,因此可以直接访问m2
    //        //结果:   m2:b = 1000        m1:b = 1000        final:b = 1000
    //    }
        
    //    public void m2() {
    //        b = 2000;                //此时m2未加锁,m1加了锁,也可以直接修改b和访问m2
    //        System.out.println("m2:b = " + b);
    //        //结果:   m2:b = 2000        m1:b = 2000        final:b = 2000
    //    }
        
        public synchronized void m2() throws InterruptedException {
            b = 2000;        //此时m2加锁,m1也加了锁,不可以直接修改b和访问m2
            System.out.println("m2:b = " + b);
            //结果:   m1:b = 1000        m2:b = 2000        final:b = 2000
        }
    
        public void run() {
            try{
                m1();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            TestLockConcept tl = new TestLockConcept();
            Thread t = new Thread(tl);
            t.start();
            
            Thread.sleep(1000);
            tl.m2();
            
            Thread.sleep(2000);
            System.out.println("final:b = " + tl.b);
        }
    } 
    View Code

    六.生产者和消费者

      实例:

    public class ProductConsumer {
    
        public static void main(String[] args) {
            SyncStack ss = new SyncStack();
            Product p = new Product(ss);
            Consumer c = new Consumer(ss);
                    //一个人每天生产40个馒头供四个人吃,每人吃十个。  
            new Thread(p).start();
            new Thread(c).start();    
            new Thread(c).start();
            new Thread(c).start();
            new Thread(c).start();
        }
    
    }
    
    class WoTou {     //窝头即是资源,有人生产窝头,有人需要窝头。
        int id;
        
        WoTou(int id){
            this.id = id;
        }
        
        public String toString(){
            return "wotou:" + id;
            
        }
    }
    
    class SyncStack {  //装窝头的篮子,最多只能装6个,提供了往里放窝头以及从里面拿窝头的方法。
        
        int index = 0;
        WoTou[] arrWT = new WoTou[6];
        
        public synchronized void push(WoTou wt){   //往篮子里放窝头
            while(index == arrWT.length){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            this.notifyAll();
            arrWT[index] = wt;
            index++;
            System.out.println("生产了:" + wt);
        }
        
        public synchronized WoTou pop(){    //从篮子里面拿窝头
            
            while(index == 0){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.notifyAll();
            index--;
            WoTou wt = arrWT[index];
            System.out.println("消费了:" + wt);
            return wt;
        }
    }
    
    class Product implements Runnable {
    
        SyncStack syncStack = null;
        
        Product(SyncStack syncStack) {
            this.syncStack = syncStack;
        }
        
        public void run() {
            for(int i=0;i<40;i++){
                WoTou wt = new WoTou(i);
                syncStack.push(wt);
                try {
                    Thread.sleep(200);     //隔一阵子生产一个窝头
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
        }
        
    }
    
    class Consumer implements Runnable {
        
        SyncStack ss = null;
        Consumer(SyncStack ss) {
            this.ss = ss;
        }
    
        public void run() {
            for(int i=0;i<10;i++){
                ss.pop();
                try{
                    Thread.sleep(1000);    //隔一阵子消费一个窝头
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
        
    }
                
    View Code
    Stay hungry,stay foolish !
  • 相关阅读:
    面向对象SOLID原则-设计模式-第2篇
    python 设计模式 开篇 第1篇
    区块链 第1篇 什么是区块链 区块链与比特币的关系 区块链的发展历史 应用领域
    HTTP协议六种请求:GET,HEAD,PUT,DELETE,POST
    python垃圾回收机制
    类的MRO属性 C3算法
    Vue第六篇 element-ui 项目管理工具npm webpack 启Vue项目vue-cli
    《EffctiveJava》泛型
    Kafka总结
    Windows 关闭端口号
  • 原文地址:https://www.cnblogs.com/jing99/p/5940566.html
Copyright © 2011-2022 走看看