线程通讯和安全

简介: 在我们实际的开发过程中,线程带来的安全问题不容我们忽视。cpu随机切换线程,我们希望cpu可以按照我们的意愿进行执行,此时就需要线程之间的通讯了。

线程同步

(1)线程安全问题

我们用代码模拟火车票窗口买票:

public class myText implements  Runnable{
    private int tickNum=100;   //一百张
    public static void main(String[] args)
    {
        myText ticket=new myText(); //售票中心
        Thread thread1=new Thread(ticket,"窗口1");
        Thread thread2=new Thread(ticket,"窗口2");
        Thread thread3=new Thread(ticket,"窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
    @Override
    public void run() {
            while (true){
                if(tickNum>0){
                    try{
                        Thread.sleep(100);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"售出第"+tickNum-- 					+"张票");
                }
            }
    }
}

出现线程安全问题,在我们实际的开发中不注意的话就会存在各种超卖

窗口1售出第3张票
窗口2售出第2张票
窗口3售出第1张票
窗口2售出第0张票
窗口1售出第-1张票

要解决以上线程问题,只要在某个线程修改共享资源的时候,其他线程不能修改该资源,等待修改完毕同步之后,才能去抢夺CPU资源,完成对应的操作,保证了数据的同步性,解决了线程不安全的现象。

1、synchronized关键字

类对象: 类的class文件

类的实例对象: 表示的类的本身实例

2、同步锁ReenreantLock

提供了比synchronized代码块和synchronized方法更广泛的锁定操作。

1、lock() //添加同步锁
2、unlock() //释放同步锁

Synchronized和Lock比较

1、Synchronized是关键字,内置语言实现,Lock是接口。
2、Synchronized在线程发生异常时会自动释放锁,因此不会发生异常死锁。Lock异常时不会自动释放锁,所以需要在finally中实现释放锁。
3、Lock可以使用读锁提高多线程读效率。
4、用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了。

(2)线程通讯

线程间的通讯的常用方法

1、休眠唤醒方式:
Object的wait、notify、notifyAll
Condition的await、signal、signalAll
2、CountDownLatch:用于某个线程A等待若干个其他线程执行完之后,它才执行
3、CyclicBarrier:一组线程等待至某个状态之后再全部同时执行
4、Semaphore:用于控制 对某组资源的访问权限

1、休眠唤醒方式

我们用两条线程配合打印奇偶数:

public class myText {
    private int a=0;
    private Lock lock=new ReentrantLock();
    private Condition oddcondition = lock.newCondition();
    private Condition evencondition = lock.newCondition();
    public  void odd() {   //打印奇数
           // lock.lock();
        synchronized(this){
           try {
               while (a <= 10) {
                   if (a % 2 == 0) {
                       //oddcondition.await();   //lock方式等待
                  		this.wait();          //obj方式
                   } else {
                       System.out.println(Thread.currentThread().getName() + "打印:" + a);
                       a++;
                       //evencondition.signal();  //lock方式唤醒
                       this.notifyAll();   //obj方式
                      
                   }
               }
           }catch (Exception e){
               e.printStackTrace();
           }finally {
               //lock.unlock();
       }
        }
   }
   public void  even() {   //打印偶数
      //lock.lock();
        synchronized(this){
           try {
               while (a <= 10) {
                   if (a % 2 == 1) {
                       //evencondition.await();
                      this.wait();
                   } else {
                       System.out.println(Thread.currentThread().getName() + "打印:" + a);
                       a++;
                       //oddcondition.signal();
                       this.notifyAll();   //obj方式
                   }
               }
           }catch (Exception e){
               e.printStackTrace();
           }finally {
               //lock.unlock();
       }
        }
   }

    public static void main(String[] args)  {
        myText myText = new myText();
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                myText.odd();
            }
        },"奇数线程");
        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {
                myText.even();
            }
        },"偶数线程");
        thread.start();
        thread1.start();
    }
}

结果:

偶数线程打印:0
奇数线程打印:1
偶数线程打印:2
奇数线程打印:3
偶数线程打印:4
......

通过wait和notifyall之间的配合实现线程按照我们的意愿执行。

1、object wait()必须在synchronized(同步锁)下使用, 
2、object wait()必须要通过Nodify()方法进行唤醒 
3、condition await() 必须和Lock(互斥锁/共享锁)配合使用
4、condition await() 必须通过 signal() 方法进行唤醒

2、CountDownLatch方式

​ java1.5被引入的,存在于java.util.concurrent包下,能够使一个线程等待其他线程完成各自的工作后再执行。通过一个计数器来实现的,计数器的初始值为线程的数量。

​ 每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

我们将上面的代码改造:

public class myText {
    private int a=0;
    private Lock lock=new ReentrantLock();
    private Condition oddcondition = lock.newCondition();
    private Condition evencondition = lock.newCondition();
    private CountDownLatch countDownLatch=new CountDownLatch(1);   //定义CountDownLatch
    public  void odd() {   //打印奇数
           // lock.lock();
           try {
               while (a <= 10) {
                   if (a % 2 == 0) {
                       //oddcondition.await();   //等待
                       countDownLatch.await();
                   } else {
                       System.out.println(Thread.currentThread().getName() + "打印:" + a);
                       a++;
                       //evencondition.signal();  //唤醒
                       countDownLatch.countDown();   //减一
                   }
               }
           }catch (Exception e){
               e.printStackTrace();
           }finally {
               //lock.unlock();
       }
   }
   public void  even() {   //打印偶数
      //lock.lock();
           try {
               while (a <= 10) {
                   if (a % 2 == 1) {
                       //evencondition.await();
                       countDownLatch.await();
                   } else {
                       System.out.println(Thread.currentThread().getName() + "打印:" + a);
                       a++;
                       //oddcondition.signal();
                       countDownLatch.countDown();
                   }
               }
           }catch (Exception e){
               e.printStackTrace();
           }finally {
               //lock.unlock();
       }
   }
    public static void main(String[] args)  {
        myText myText = new myText();
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                myText.odd();
            }
        },"奇数线程");
        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {
                myText.even();
            }
        },"偶数线程");
        thread.start();
        thread1.start();
    }
}

3、CyclicBarrier

在java1.5被引入的,存在于java.util.concurrent包下。实现让一组线程等待至某个状态之后再全部同时执行

public class myText {
    public static void main(String[] args)  {
        final CyclicBarrier cb=new CyclicBarrier(3);   //定义线程数量
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println("1号准备。。。");
                    cb.await();  //减一
                    System.out.println("1号结束。。。");
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println("2号准备。。。");
                    cb.await();  //减一
                    System.out.println("2号结束。。。");
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        Thread thread3=new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    Thread.sleep(1000);
                    System.out.println("3号准备。。。");
                    cb.await();  //减一
                    System.out.println("3号结束。。。");
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        thread2.start();
        thread3.start();
    }
}

4、Semaphore

用于控制对某组资源的访问权限。

  • **acquire:**请求
  • **release:**释放
public class myText {
    static class Machine implements Runnable{
        private int num;
        private   Semaphore semaphore;

        public Machine(int num, Semaphore semaphore) {
            this.num = num;
            this.semaphore = semaphore;
        }
        public void run() {
            try {
                semaphore.acquire();//请求机器
                System.out.println("工人"+this.num+"请求机器,正在使用机器");
                Thread.sleep(1000);
                System.out.println("工人"+this.num+"使用完毕,已经释放机器");
                semaphore.release();//释放机器
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args){
        int worker = 8;//工人数
        Semaphore semaphore = new Semaphore(3);//机器数
        for (int i=0; i< worker; i++){
            new Thread(new Machine(i, semaphore)).start();
        }
    }

}
工人2请求机器,正在使用机器
工人0请求机器,正在使用机器
工人3请求机器,正在使用机器
工人0使用完毕,已经释放机器
工人3使用完毕,已经释放机器
工人2使用完毕,已经释放机器
工人7请求机器,正在使用机器
工人6请求机器,正在使用机器
工人4请求机器,正在使用机器
工人6使用完毕,已经释放机器
工人1请求机器,正在使用机器
工人4使用完毕,已经释放机器
工人7使用完毕,已经释放机器
工人5请求机器,正在使用机器
工人5使用完毕,已经释放机器
工人1使用完毕,已经释放机器

完美控制了资源的访问权限。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×