多线程同步

一:多线程访问临界资源问题(为何加锁的原因)

     例如下面代码:

           如果一个线程在取值的过程中,即已经获取ticket 的当前值,后时间片又被其它线程抢走,此时后来者县线程ticket值与上一个一样,这样就有重复了。。

          如果加锁,一次只能进来一个线程,就安全了


public class Demo1 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket,"w1").start();
new Thread(ticket,"w2").start();
new Thread(ticket,"w3").start();
new Thread(ticket,"w4").start();
}
}
public class Ticket implements Runnable{
int ticket= 100;


@Override
public void run() {
while (true){
if (ticket < 1) break;
System.out.println(Thread.currentThread().getName()+" 正在售卖第"+ticket+"张票");
ticket--;
}
}
}
打印结果:
w1   正在售卖第100张票
w1   正在售卖第99张票
w1   正在售卖第98张票 xxxx
w2   正在售卖第98张票 xxxx
w2   正在售卖第96张票
w2   正在售卖第95张票
w2   正在售卖第94张票 xxxxx
w3   正在售卖第94张票 xxxxx
w3   正在售卖第92张票


 

      锁:

                 锁的概念,任意对象都可以当作锁,锁多与同步代码块、同步方法并用

        【第一种】:         

                           关键字 : syncronized

                              synchronized(锁) {
                                       //需要访问临界资源的代码段
                                }

    案例(同步代码块)

  
synchronized (object){ if (ticket < 1) break; System.out.println(Thread.currentThread().getName()+" 正在售卖第"+ticket+"张票"); ticket--; }
注:一定要用同一个锁(即必须是同一个对象),否则不是同一个同步代码块

 

      案例(同步代码方法)

 

 1 public class Ticket implements Runnable{
 2     int ticket= 100;
 3     Object object = new Object();
 4 
 5     @Override
 6     public void run() {
 7         while (true){
 8             if (!synchronized2()){
 9                 break;
10             }
11 
12         }
13     }
14     此时用的锁是当前对象  this
15     private synchronized boolean synchronized2() {
16             if (ticket < 1) return false;
17             System.out.println(Thread.currentThread().getName()+"   正在售卖第"+ticket+"张票");
18             ticket--;
19             return true;
20     }
21 }

 

【第二种】

            关键字:ReentrentLock (接口,实现类Lock  jdk1.5出现的)

            总共需要三步:

                              1.创建锁对象  --> Lock lock = new ReentrentLock();

                              2.加锁                   lock.lock();

                              3.解锁                   lock.unlock();(最好在finally中是实现)

                                注:1、2 步是成对出现

 

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Ticket implements Runnable{
    int ticket= 100;
    Lock lock = new ReentrantLock();  //第一步
    @Override
    public void run() {
        while (true){           
            lock.lock();   //第二部
            try {
                if (ticket < 1){
                    break;
                }
                System.out.println("shou"+ticket);
                ticket--;
            } finally {

                lock.unlock();  //第三步
            }   
        }
    }
}

 

第二节 死锁

两个人公用一根筷子,甲拿着一根等已手里的那根,已等着甲手里那根-形成死锁-----------------------------------------------俗话说

死锁的条件:

               1两个以上的线程

               2至少两个锁以上

               3同步中嵌套同步

         

public static void main(String[] args) {
        Boy shaqiang=new Boy();
        Girl xiaofeng=new Girl();
        shaqiang.start();
        xiaofeng.start();
    }
/*
 * 女孩
 */
public class Girl extends Thread{
    @Override
    public void run() {
        while (true) {
            synchronized (Lock.lockb) {
                System.out.println("女孩拿着lockb");
                synchronized (Lock.locka) {
                    System.out.println("女孩拿到了locka");
                    System.out.println("女孩可以吃了...");
                }
            } 
        }
    }
}
/*
*男孩
*/
public class Boy extends Thread{
    @Override
    public void run() {
        while (true) {
            synchronized (Lock.locka) {
                System.out.println("男孩拿着locka");
                synchronized (Lock.lockb) {
                    System.out.println("男孩拿到lockb");
                    System.out.println("男孩可以吃了....");
                }
            } 
        }
    }
}
public class Lock {
    public static Object locka=new Object();//第一个锁
    public static Object lockb=new Object();//第二个锁
}

第三: 单例模式(懒汉式)完善

           第一种改善方式:加锁

           第二种改善方式:静态内部类

           第三种改善方式:枚举

 

第一种改善方式:加锁


//此种方式效率高,什么时候调用什么时候创建对象但是线程不安全,两个线程先后进入 “ if (myone == null){”就会产生两个对象
public class MyOne { private MyOne(){} private static MyOne myone = null; public static MyOne getInstance(){ if (myone == null){ myone = new MyOne(); } return myone; } }

//此种方式效率不高,虽然什么时候调用什么时候创建对象线程安全,但是当第一次创建成功对象后,都会先进行判断锁的过程比较耗性能,为了提高效率
public
class MyOne { private MyOne(){} private static MyOne myone = null; public static MyOne getInstance(){ synchronized (MyOne.class){ if (myone == null){ myone = new MyOne(); } } return myone; } }

//此种方式效率高,什么时候调用什么时候创建对象线程安全,但是当第一次创建成功对象后,先判断是否已经创建对象,如果已经创建了,就你进行判断锁了
public class MyOne {
private MyOne(){
//禁止反射破解
synchronized (SingleTon.class) {
if (instance != null) {
throw new RuntimeException("不能使用反射创建对象");
}
}

}
private static MyOne myone = null;
public static MyOne getInstance(){
if (myone == null) {
synchronized (MyOne.class) {
if (myone == null) {
myone = new MyOne();
}
}
}
return myone;
}
}


 

                      

第二种改善方式:静态内部类

 1 /*
 2  * 静态内部类写法
 3  * (1)节省空间
 4  * (2)不会线程安全问题
x * 内部类不会再外部类加载时就加载,而是在创建内部类对象时加载,所以内部类中的单例对象何时调用何时加载
5 */ 6 public class SingleTon2 { 7 private SingleTon2(){} 11 static class Holder{ 12 private static final SingleTon2 INSTACNE=new SingleTon2(); 13 } 15 public static SingleTon2 getInstance(){ 16 return Holder.INSTACNE; 17 } 19 }

 

第三种改善方式:枚举

/*
 * 枚举写法
 * (1)没有线程安全问题
 * (2)反射破解问题
 *
 */
public enum  SingleTon3 {
    INSTANCE;
    public static SingleTon3 getInstance(){
        return INSTANCE;
    }
}

 

第四节 线程的通信【生产者与消费者设计模式】

   4.1)线程通信

开发中不免会遇到需要所有子线程执行完毕通知主线程处理某些逻辑的场景。

或者是线程 A 在执行到某个条件通知线程 B 执行某个操作。

在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作。比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作。

  Java中线程通信协作的最常见的两种方式:

  一.syncrhoized加锁的线程的Object类的wait()/notify()/notifyAll()

  二.ReentrantLock类加锁的线程的Condition类的await()/signal()/signalAll()

    

public class Demo4 {
    public static void main(String[] args) {
        BankCard card = new BankCard();
        AddMoney addMoney = new AddMoney(card);
        SubMoney subMoney = new SubMoney(card);
        new Thread(addMoney,"张三").start();
        new Thread(subMoney,"李四").start();
    }
}

public class BankCard {
    private int money;
    private boolean flag = false;
    public int getMoney() {
        return money;
    }
    public void setMoney(int money) {
        this.money = money;
    }
    public synchronized void take() {   



//此处注意,因为 this.wait(),处于等待状态,但是当被唤醒仍然出此处开始往下执行,就是不需要在判断是否已经有钱了,所以不if改成while,让它唤醒后仍然判断 //
if (!flag){//没钱


while(!flad){ try { this.wait(); //等待,释放资源、释放锁 } catch (InterruptedException e) { e.printStackTrace(); } } money -=1000; System.out.println("取钱1000 剩余"+money); flag = false; this.notifyAll();//唤醒 } public synchronized void save() { if (flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } money +=1000; System.out.println("存钱1000 "+money); flag = true;
//此处 容易发生死锁现行容易发生唤醒错误,所以都唤醒用this.notifyAll();
//this.notify();
this.notifyAll(); } } public class AddMoney implements Runnable { private BankCard card; public AddMoney(BankCard card) { this.card = card; } @Override public void run() { for (int i = 0; i < 10; i++) { card.save(); } } } public class SubMoney implements Runnable { private BankCard card; public SubMoney(BankCard card) { this.card = card; } @Override public void run() { for (int i = 0; i < 10; i++) { card.take(); } } }

 

                                                                                              注:在此案例中 此处this.notifyAll(); 如果使用this.notify();容易发生死锁现象,即四个对象丢都进入栈中,没有人唤醒

4.2 生产者与消费者设计模式原理

      鄙人理解不深,用很俗的话语解释,就是一家售货店,它能容货物十件,你可以加货但只添加1--10,售货也只能出售进货的数量,如果没货了,你不能售了,你可以让买家(this.wait()),等待的时候就可以进货,进完货进行再让买家买货(唤醒)
public class Demo1 {
    public static void main(String[] args) {
        BreadContainer container = new BreadContainer();
        Consumer consumer = new Consumer(container);
        produer produer1 = new produer(container);
        new Thread(consumer,"1").start();
        new Thread(produer1,"2").start();
    }
}
public class BreadContainer {
    private Bread[] arr= new Bread[6];
    private Bread bread;
    private int index = -1;

    public BreadContainer() {
    }
    public Bread[] getArr() {
        return arr;
    }
    public void setArr(Bread[] arr) {
        this.arr = arr;
    }
    public Bread getBread() {
        return bread;
    }
    public void setBread(Bread bread) {
        this.bread = bread;
    }
    public BreadContainer(Bread[] arr, Bread bread) {
        this.arr = arr;
        this.bread = bread;
    }
    public synchronized void add() {
        while (index >= 5){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        index++;
        arr[index] = new Bread();
        System.out.println("添加一个,余额"+index);
        this.notifyAll();
    }
    public synchronized void sub() {
        while (index <= 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        arr[index] = new Bread();
        index--;
        System.out.println("出售一个,余额"+index);
        this.notifyAll();
    }
}
public class produer implements Runnable {
    private BreadContainer container;

    public produer(BreadContainer container) {
        this.container = container;
    }

    public BreadContainer getContainer() {
        return container;
    }

    public void setContainer(BreadContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            container.sub();
        }
    }
}

public class Consumer implements Runnable{
    private BreadContainer container;

    public BreadContainer getContainer() {
        return container;
    }

    public void setContainer(BreadContainer container) {
        this.container = container;
    }

    public Consumer(BreadContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            container.add();
        }
    }
}

 

 

 

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BreadContainer {
    private Bread[] arr= new Bread[6];
    private int index = -1;
    private Lock lock = new ReentrantLock();
    Condition pro_con = lock.newCondition();
    Condition con_con = lock.newCondition();
    public void add() {
        lock.lock();
        while (index >= 5){
            try {
                pro_con.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        index++;
        arr[index] = new Bread();
        System.out.println("增加    "+index);
        con_con.signalAll();
        lock.unlock();
    }

    public void sub() {
        lock.lock();
        while (index <= 0){
            try {
                con_con.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        arr[index]= null;
        index--;
        System.out.println("减少  "+index);
        pro_con.signalAll();
        lock.unlock();
    }
}

 

posted @ 2019-08-11 17:06  lcj12121  阅读(193)  评论(0)    收藏  举报