多线程初阶2(解决线程安全问题,线程间的通信)

·多线程安全问题

一.观察线程不安全

代码示例:

public class ThreadDemon2 {
    private static class Counter{
        private long n=0;

        public  void increment(){
            n++;
        }

        public long value(){
            return n;
        }
    }

    public static void main(String[] args) throws InterruptedException{
        final int COUNT=10000;
        Counter counter=new Counter();

        //新建一个线程执行n++操作
        Thread thread=new Thread(()->{
            for(int i=0;i<COUNT;i++){
                counter.increment();
            }
        },"李四");
        thread.start();


        //在main函数里执行n++操作
        for(int i=0;i<COUNT;i++){
            counter.increment();
        }

        //等待thread执行完毕
        thread.join();
        //输出值
        System.out.println(counter.value());
    }
}


运行结果:
在这里插入图片描述
  运行结果并不是20000,也不唯一,之所以会出现上述情况是因为n++操作不是原子性的
实际上n++/n–操作是分为三步来执行的:
 ①从主内存复制n的值到线程的工作内存
 ②在工作内存中完成对n的++/–操作
 ③将n值写回主内存
 试想若在n值还没有写回主内存之前,有别的线程拷贝主内存的值,那它拷贝的就是修改前的值,这样共享变量发生了修改的丢失造成了线程不安全

  

二.线程不安全原因

并发编程特性

三.解决线程不安全

 对上述不安全的代码进行加锁操作
代码示例:

public class ThreadDemon2 {
    private static class Counter{
        private long n=0;

        public  synchronized void increment(){
            n++;
        }

        public long value(){
            return n;
        }
    }

    public static void main(String[] args) throws InterruptedException{
        final int COUNT=10000;
        Counter counter=new Counter();

        //新建一个线程执行n++操作
        Thread thread=new Thread(()->{
            for(int i=0;i<COUNT;i++){
                counter.increment();
            }
        },"李四");
        thread.start();


        //在main函数里执行n++操作
        for(int i=0;i<COUNT;i++){
            counter.increment();
        }

        //等待thread执行完毕
        thread.join();
        //输出值
        System.out.println(counter.value());
    }
}

运行结果:
在这里插入图片描述
同步块在已进入的线程执行完之前会阻塞后面其他线程的进入




·synchronized关键字

一.锁的SynchronizedDemo对象

public class SynchronizedDemo {
    public synchronized void methond(){
    }

    public static void main(String[] args) {
        SynchronizedDemo demo=new SynchronizedDemo();
        demo.methond();  //进入方法会锁demo指向对象中的锁,出方法会释放demo指向对象中的锁
    }
}


二.锁的SynchronizedDemo类的对象

public class SynchronizedDemo {
    public synchronized static void methond(){
    }

    public static void main(String[] args) {
        methond();      //进入方法会锁SynchronizedDemo.class指向对象中的锁,出方法会释放SynchronizedDemo.class指向对象中的锁
    }
}


三.明确锁的对象

public class SynchronizedDemo {
   public void methond(){

       //进入代码块会锁this指向对象中的锁,出代码块会释放this指向对象中的锁
       synchronized (this){

       }
   }

    public static void main(String[] args) {
        SynchronizedDemo demo=new SynchronizedDemo();
        demo.methond();
    }
}




public class SynchronizedDemo {
   public void methond(){

       // //进入代码块会锁SynchronizedDemo.class指向对象中的锁,出代码块会释放SynchronizedDemo.class指向对象中的锁
       synchronized (SynchronizedDemo.class){

       }
   }

    public static void main(String[] args) {
        SynchronizedDemo demo=new SynchronizedDemo();
        demo.methond();
    }
}




·volatile关键字

  保证可见性,保证有序性,不能保证原子性

class ThreadDemo{
    private volatile int n;
}




·线程间的通信

一.wait()方法

 1.wait()方法就是使线程停止运行,让当前线程进入等待状态,同时也会让当前线程释放掉它所持有的锁。直到其他线程调用此对象的notify()方法或notifyAll()方法,当前线程被唤醒,进入就绪状态。
 2.wait()方法只能在同步方法中或同步块中调用,如果调用wait()时,没有持有适当的锁,就会抛出异常

代码示例:

public class ThreadDemo3 {
    public static void main(String[] args) throws InterruptedException{
        Object object=new Object();
        synchronized (object){
            System.out.println("等待中.....");
            object.wait();
            System.out.println("等待时间到");
        }
        System.out.println("main方法结束");
    }
}

运行结果:
在这里插入图片描述
  上述同步代码块中调用了wait()方法后会一直都是等待中,因为没有线程将它唤醒
  

二.notify()方法

 1.方法notify()就是唤醒线程,也要在同步方法或者同步代码块中调用,该方法用来通知那些可能等待该对象的对象锁的其他线程,对其发出notify(),并使它们重新获取该对象的对象锁。如果有多个线程等待,则有线程规划器随机挑出一个wait()状态的线程
 2.在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码之后才会释放对象锁
  

三.notifyAll()方法

 notify()方法只是唤醒某一个等待线程,notifyAll()方法可以一次唤醒所有的等待线程
  

四.wait()和sleep()的对比

 1.wait要用在同步方法或者同步代码块中,wait执行时会先释放掉锁,等待被唤醒时再重新请求锁
 2.sleep是无视锁的存在的,请求之后锁不会释放,没有锁也不会请求
 3.wait是Object方法,sleep是Thread类的静态方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值