同步

线程中的问题

竞态条件

         当计算机的正确性取决于相对时间或者调度器所控制的多线程交叉时,竞态条件就会发生。

  1. check-then-act  检查后行动

If(a==0){

   B++;

}

 

  1. read-motify-write 读-改-更新

Public int getId(){

   Return  count++;

}

 

数据竞争

         2条或者2条以上的线程(在单个应用)并发访问同一块内存区域,同事其中至少有一条是为了写,而且这条线程没有对那块内存区域的访问。依照这种顺畅,每次运行都可能会产出不同的结果。

一个动作先于另一个动作发生,数据竞争产生了。

缓存变量

每条线程都有自己的变量拷贝,当线程写入这个变量的时候,其实是写入自己拷贝;其他线程不太可能在看到自己的变量拷贝发生改变。

同步临界区的访问

临界区就是必须串行访问一段代码。

同步是通过监听器来实现的。每个java对象都和一个监听器关联,这样线程就可以通过获取和释放监听的锁来上锁和解锁。

*注意:Theard d调用sleep()方法,已经获取锁的线程不会释放锁。

只有一个线程可以只有监听器的锁,任意尝试锁住监听器的线程都会一直阻塞,直到能获取锁位置。

 

互斥性:因为其他线程在临界区中的时候,每条线程对该临界区的访问都是互斥的执行。

线程获取的锁经常成为互斥锁。

可见性:该属性能够保证一条线程在临界区执行的时候,总是能看到共享变量最近的修改。

 

使用同步方法

方法前加synchronized

使用同步块

Synchronized(object)

{

 

}

谨防活跃性问题

死锁:线程1等待线程2互斥持有的资源,而 在等待线程互斥只有的资源。2条线程都无法继续执行。

一般发生在synchronized关键字带来过多的同步上。尽量不使用,从而避免死锁。

 

活锁;线程X持续重试一个总是失败的操作,以至于无法持续执行。

饿死:线程X一直被延迟访问其依赖执行的资源。

 

Volatile 和 final 变量

通过volatile关键字停止一个线程

public class MyThread11 {

 

   public MyThread11() {

 

   }

 

   public static void main(String[] args) {

      @SuppressWarnings("unused")

      class StopThread extends Thread {

        private volatile boolean stopped;

 

        @Override

        public void run() {

           while (!stopped) {

              System.out.println("running");

           }

        }

 

        void stopThread() {

           stopped = true;

        }

      }

      StopThread st = new StopThread();

      st.start();

      try {

        Thread.sleep(1000);

      } catch (Exception e) {

        // TODO: handle exception

      }

      st.stopThread();

 

   }

}

由于stopped 已经标记为volatile,每条线程都会访问主寸中该变量的拷贝 而不会被访问缓存中的拷贝。这样即使在处理器或者多喝的机器上,该线程也会被停止。

Volatile 修饰的变量, 不能同时被final 声明。

警告

只有可见性导致问题时,才应该使用volatile。而且,你也只能在属性上使用这个保留字(类变量)。最后,你可以将double和long型的属性声明成volatile,但是应该避免在32位的jvm,原因是此时的访问一个double或者long型的变量值需要进行两步操作,若要安全的访问他们的值,互斥(synchronize)是必要的

 

 

Java可以让你安全的访问final属性而不需要同步。 Final 类为线程安全的类。

创建一个不可变类

/**

 * Project Name:demo

 * File Name:MyThread03.java

 * Package Name:two

 * Date:2019年8月27日下午6:58:01

 * Copyright (c) 2019, 364574851@qq.com All Rights Reserved.

 *

*/

 

package one;

 

import java.util.Set;

import java.util.TreeSet;

 

/**

 * ClassName:MyThread03 <br/>

 * Function: TODO ADD FUNCTION. <br/>

 * Reason: TODO ADD REASON. <br/>

 * Date: 2019年8月27日 下午6:58:01 <br/>

 *

 * @author zhangxdp

 * @version

 * @since JDK 1.8

 * @see

 */

public final class Planets {

 

         private final Set<String> planets = new TreeSet<>();

 

         public Planets() {

                   planets.add("Mercury");

                   planets.add("Venus");

                   planets.add("Earth");

                   planets.add("Mars");

                   planets.add("Jupiter");

                   planets.add("Saturn");

                   planets.add("Uranus");

                   planets.add("Neptune");

         }

 

         public boolean isPlanet(String planetName) {

                   return planets.contains(planetName);

         }

}

 

不可变队形提供了易于识别的规则:

1、  不可变队形绝对不允许状态变更。

2、  所有的属性必须声明成final。

3、  队形必须被恰当地构造出来以防止this引用脱离构造函数。

例子如下

public class ThisDemo {

   public static ThisDemo td;

 

   public ThisDemo() {

      td = this;

   }

}

posted on 2019-09-12 14:08  不在是从前  阅读(191)  评论(0)    收藏  举报

导航