并发编程1

昨天回到北京,年前就把工作辞掉了,现在正是失业状态,本想着今天看点东西面试,犯懒了,哎

线程安全的概念

当多个线程访问一个类(对象或者方法)时,这个类始终都能表现出正常的行为,那么这个类(对象,方法)就是线程安全的(这个解释有点搞笑!

Synchronied

可以再任意对象和方法上加锁。而加锁的这段代码称为互斥区或者叫临界区。

锁竞争的问题

是指在同步的时候,当一个线程运行完毕释放资源,其他线程抢资源会导致CPU使用率暴增引起机器高负荷运行,导致的问题:1程序运行缓慢,2程序崩溃,3更甚者导致服务器宕机。

因此,在程序的编写的过程中要避免锁竞争的问题。极端举例,当五个线程同时竞争还能hold住,当线程超过1k或者说线程到1w,这种问题就会暴露出来,危险!

多个线程多个锁的问题

锁也分对象的锁,类的锁。

 

1、加对象锁实现同步

package threadTest.多锁;

 

/**

 * 单锁,对象锁

 * @author Administrator

 *

 */

publicclass Lock {

 

      privateintflag = 0;

     

      Object key = null;

      public Lock(Object key) {

           this.key = key;

      }

     

      publicvoid setTag(String tag){

           synchronized(key){

           if("a".equals(tag)){

                 flag = 200;

                 System.out.println("set tag,"+flag);

           }else {

                 flag = 300;

                 System.out.println("set tag,"+flag);

           }

           try {

                 Thread.sleep(1000);

           } catch (InterruptedException e) {

                 e.printStackTrace();

           }

           System.out.println("tag="+ tag+",flag="+flag);

           }

      }

     

      publicstaticvoid main(String[] args) {

           Object key = new Object();

          

           final Lock m1 = new Lock(key);

           final Lock m2 = new Lock(key);

          

           Thread t1 = new Thread(new Runnable() {

                 @Override

                 publicvoid run() {

                      m1.setTag("a");

                 }

           });

          

           Thread t2 = new Thread(new Runnable() {

                 @Override

                 publicvoid run() {

                      m2.setTag("b");

                 }

           });

          

           t1.start();

           t2.start();

 

      }

 

}

 

2、加类级锁实现同步,操作方法加入static

package threadTest.多锁;

 

/**

 * 类锁

 * @author Administrator

 *

 */

publicclass ClassLock {

 

      privatestaticintflag = 0;

     

      Object key = null;

      public ClassLock(Object key) {

           this.key = key;

      }

     

      public ClassLock() {

           super();

      }

 

      publicstaticsynchronizedvoid setTag(String tag){

           if("a".equals(tag)){

                 flag = 200;

                 System.out.println("set tag,"+flag);

           }else {

                 flag = 300;

                 System.out.println("set tag,"+flag);

           }

           try {

                 Thread.sleep(1000);

           } catch (InterruptedException e) {

                 e.printStackTrace();

           }

           System.out.println("tag="+ tag+",flag="+flag);

          

      }

     

      publicstaticvoid main(String[] args) {

          

           final ClassLock m1 = new ClassLock();

           final ClassLock m2 = new ClassLock();

          

           Thread t1 = new Thread(new Runnable() {

                 @Override

                 publicvoid run() {

                      m1.setTag("a");

                 }

           });

          

           Thread t2 = new Thread(new Runnable() {

                 @Override

                 publicvoid run() {

                      m2.setTag("b");

                 }

           });

          

           t1.start();

           t2.start();

 

      }

 

}

 

 

 

同步synchronized

同步的概念就是共享,如果不是共享的资源,就没必要进行同步。(这里要分清原子性和同步的概念,目前我理解的原子性还是,操作不可再分,同步可以让一个组操作完整执行而不被其他线程打断,个人理解,以后还要继续优化

异步asynchronized

异步的概念是独立,相互之间不受到任何的制约。对java来说异步就是多个线程可以并发的访问没有加同步手段的方法,变量。而同步的方法多个线程要排队一个一个的访问。

 

 

同步的目的是线程安全,对于线程安全来说,需要满足两个特性:原子性(同步),可见性

synchronized使用技巧:

publicclass Syn {

    publicsynchronizedvoid meth1(){

        System.out.println("meth01");

        meth2();

    }

    publicsynchronizedvoid meth2(){

        System.out.println("meth02");

        meth3();

    }

    publicsynchronizedvoid meth3(){

        System.out.println("meth03");

    }

    publicstaticvoid main(String[] args) {

        final Syn syn = new Syn();

        Thread t1 = new Thread(new Runnable() {

            publicvoid run() {

                syn.meth1();

            }

        });

        t1.start();

    }

}

 

synchronized的方法可以无阻赛调用。理解:

有点锁域的问题:

换一种写法更容易理解;

Object object = new Object();

    publicvoid meth1(){

       synchronized (object) {

           System.out.println("meth01");

           //meth2();

           synchronized (object) {

              System.out.println("meth02");

              //meth3();

              synchronized (object) {

                  System.out.println("meth03");

              }

           }

       }

    }

在同步的时候,同步区运行错误怎么处理?

第一种情况,继续运行方式

在一个同步块里,出错的执行与这个块里面的其他的执行没关系,可以采用try-catch方式捕获出错的执行,记录在日志或者其他里面,然后continue继续执行,待其他的执行完毕后再单独处理这个出错的执行。

第二种情况,中断运行

这种情况的环境前提是,执行区里面的所有的执行都是相互依赖的,同生共死的关系,一个运行失败时抛出打断异常,interruptedException,或者在catch里面throw new runtimeException();都可以中断。然后回滚前面的数据。

 

synchronized里面的锁

publicclass SynLock {

 

      publicvoid method1(){

           synchronized (this) {  //对象锁

                 System.out.println("method1");

           }

      }

      publicvoid method2(){

           synchronized (SynLock.class) {  //类锁

                 System.out.println("method2");

           }

      }

      publicvoid method3(){

           Object object =new Object();

           synchronized (object) {  //任意对象锁

                 System.out.println("method3");

           }

      }

      publicstaticvoid main(String[] args) {

 

      }

 

}

注意:在加锁的时候尽量不要出现用字符串常量加锁,会出现死循环的问题(这段代码的结构有问题,不能说明问题,线程1拿到锁资源后一直没释放,导致线程2没进来。),还有字符串的在作为锁的时候不能修改字符串的值,一旦修改他就会指向新的对象,原来的锁就会释放,其他线程就会进来(相当于锁坏了,不能限制排斥其他线程了)

publicclass stringLock {

 

      String s="ce";

      publicvoid method(){

           synchronized (s) {

                 try {

                      while (true) {

                            System.out.println(Thread.currentThread().getName()+",开始");

                            Thread.sleep(1000);

                            System.out.println(Thread.currentThread().getName()+",结束");

                      }

                 } catch (InterruptedException e) {

                      e.printStackTrace();

                 }

           }

      }

     

      publicstaticvoid main(String[] args) {

           Object object = new Object();

           final stringLock lock = new stringLock();

           Thread t1 = new Thread(new Runnable() {

                 @Override

                 publicvoid run() {

                      lock.method();

                 }

           },"t1");

           Thread t2 = new Thread(new Runnable() {

                 @Override

                 publicvoid run() {

                      lock.method();

                 }

           },"t2");

          

           t1.start();

           t2.start();

 

      }

 

}

volatile再次理解

概念:实现变量在多个线程间可见。

验证变量不可见的反例子

publicclass volatileTset extends Thread {

   

    privatebooleanisRunning = true;

    publicboolean isRunning() {

        returnisRunning;

    }

    publicvoid setRunning(booleanisRunning) {

        this.isRunning = isRunning;

    }

 

 

    @Override

    publicvoid run() {

        System.out.println("进入Run");

        while(isRunning){

           

        }

        System.out.println("终止Run");

    }

 

    publicstaticvoid main(String[] args) throws InterruptedException {

        volatileTset v = new volatileTset();

       

        v.start();

        v.sleep(3000);

        v.setRunning(false);

        System.out.println("设置了isrunning");

        v.sleep(1000);

        System.out.println(v.isRunning());

 

    }

 

}

这里虽然改变了isRunning的值但是,线程并没有停止,说明线程的工作内存里的isRunning的值并没有改变。

volatile修饰后的变量,他会强制线程在用到这个变量的时候到主内存中去读取这个变量,并且把值加载到工作内存中去

原子类的使用

publicclass AtomicTest extends Thread {

    //加入了static 使这个变量变成共有的变量这个变量是类的

    privatestaticvolatile AtomicInteger count = new AtomicInteger(0);

    privatestaticvoid add(){

        for(inti = 0;i < 100;i++){

            count.getAndIncrement();

        }

        System.out.println(count);

    }

    @Override

    publicvoid run() {

            add();

    }

 

    publicstaticvoid main(String[] args) {

        AtomicTest[] arr = new AtomicTest[10];

        for(inti = 0;i<10;i++){

            arr[i] =new AtomicTest();

        }

        for(inti = 0;i<10;i++){

            arr[i].start();

        }

    }

}

如果原子类有多个方法一起使用,它只能保证一个方法的原子性。

 

线程之间的通信

使用wait/notify方法实现线程的通信,

1、  wait和notify必须配合synchronized关键字使用(配合一些锁使用也可以)

2、  wait方法释放锁,notify方法不释放锁

 

 

通信渐入

设计需求:

定义一个数组A,线程t1添加元素,t2发现A的大小超过5时就抛出异常终止运行。

publicclass Waitnotify {

     

      privatevolatilestatic ArrayList list = new ArrayList();

      privatevolatilestatic  inta= 0;

      publicvoid add(){

           list.add("ceshi");

      }

      publicint getLength(){

           returnlist.size();

      }

     

      publicstaticvoid main(String[] args) {

 

           final Waitnotify w= new Waitnotify();

           final Object object = new Object();

          

           Thread t1 = new Thread(new Runnable() {

                 publicvoid run() {

                      synchronized (object) {

                                  try {

                                       for(inti = 0;i<10;i++){

                                       w.add();

                                       System.out.println(Thread.currentThread().getName()+"添加了一个元素");

                                       Thread.sleep(500);

                                       }

                                  } catch (InterruptedException e) {

                                       e.printStackTrace();

                                  }

                      }

                 }

           },"t1");

           Thread t2 = new Thread(new Runnable() {

                 publicvoid run() {

                      while(true){

                            if(w.list.size()>5){

                                  System.out.println(Thread.currentThread().getName()+"停止线程");

                                  thrownew RuntimeException();

                            }

                      }

                 }

           },"t2");

          

           t1.start();

           t2.start();

          

      }

}

 

优化这段代码

现在加入了锁,和synchronize控制,讲解:程序先执行了t2,启动后拿到锁资源,发现数组长度并没有到5,锁进行了wait(),程序就停止在wait()的这一行,同时释放掉锁资源,同时t1拿到了锁的资源进行执行添加的操作,当添加到5的时候,进行唤醒另一个线程notify(),但是,此刻的锁仍在t1这里,所以t1仍然继续执行,知道t1执行完毕释放了锁,t2才继续执行停止线程。

问题:t2没法时时性得到通知

publicclass Waitnotify {

 

      privatevolatilestatic ArrayList list = new ArrayList();

      privatevolatilestaticinta = 0;

 

      publicvoid add() {

           list.add("ceshi");

      }

 

      publicint getLength() {

           returnlist.size();

      }

 

      publicstaticvoid main(String[] args) {

 

           final Waitnotify w = new Waitnotify();

           final Object object = new Object();

 

           Thread t1 = new Thread(new Runnable() {

                 publicvoid run() {

                      synchronized (object) {

                            try {

                                  for (inti = 0; i < 10; i++) {

                                       w.add();

                                       System.out.println(Thread.currentThread().getName() + "添加了一个元素");

                                       Thread.sleep(500);

                                       if (w.list.size() == 5) {

                                             System.out.println("发出通知~~");

                                             object.notify();

                                       }

                                  }

                            } catch (InterruptedException e) {

                                  e.printStackTrace();

                            }

                      }

                 }

           }, "t1");

           Thread t2 = new Thread(new Runnable() {

                 publicvoid run() {

                      synchronized (object) {

 

                            try {

                                  if (w.list.size() != 5) {

                                       object.wait();

                                  }

                                  System.out.println(Thread.currentThread().getName() + "停止线程");

                                  thrownew RuntimeException();

                            } catch (InterruptedException e) {

                                  e.printStackTrace();

                            }

                      }

                 }

           }, "t2");

           t2.start();

           t1.start();

      }

 

}

 

继续优化:利用java.util.concurrent  下的CountDownLatch,利用await(),与countdown()进行合理利用,具体解释看jdkapi的解释,此时可以达到时时的结束程序

publicclass Waitnotify {

 

      privatevolatilestatic ArrayList list = new ArrayList();

      privatevolatilestaticinta = 0;

 

      publicvoid add() {

           list.add("ceshi");

      }

 

      publicint getLength() {

           returnlist.size();

      }

 

      publicstaticvoid main(String[] args) {

 

           final Waitnotify w = new Waitnotify();

           //final Object object = new Object();

           final CountDownLatch latch = new CountDownLatch(1);

 

           Thread t1 = new Thread(new Runnable() {

                 publicvoid run() {

                      //synchronized (object) {

                            try {

                                  for (inti = 0; i < 10; i++) {

                                       w.add();

                                       System.out.println(Thread.currentThread().getName() + "添加了一个元素");

                                       Thread.sleep(500);

                                       if (w.list.size() == 5) {

                                             System.out.println("发出通知~~");

                                             //object.notify();

                                             latch.countDown();

                                       }

                                  }

                            } catch (InterruptedException e) {

                                  e.printStackTrace();

                            //}

                      }

                 }

           }, "t1");

           Thread t2 = new Thread(new Runnable() {

                 publicvoid run() {

                      //synchronized (object) {

 

                            try {

                                  if (w.list.size() != 5) {

                                  //    object.wait();

                                       latch.await();

                                  }

                                  System.out.println(Thread.currentThread().getName() + "停止线程");

                                  thrownew RuntimeException();

                            } catch (InterruptedException e) {

                                  e.printStackTrace();

                            }

                      }

                 //}

           }, "t2");

           t2.start();

           t1.start();

      }

 

}

 

 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西门吹水之城

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值