线程同步

一.简介

  在一般情况下,创建一个线程是不能提高程序的执行效率的,所以要创建多个线程。但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于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 !
原文地址:https://www.cnblogs.com/jing99/p/5940566.html