微网站如何建立,自己做捕鱼网站能不能挣钱,公司网站建设需求书,广州汽车网络推广服务首先要记住核心一点、多线程事异步的#xff0c;也就是cpu利用率大幅提高。 Stringbuffer 是线程安全的 stringbuilder是线程不安全的HashTable是线程安全的 HashMap不是线程安全的 2.对象及变量的并发访问下的问题。 方法内的变量因为是方法的私有变量#xff0…首先要记住核心一点、多线程事异步的也就是cpu利用率大幅提高。 Stringbuffer 是线程安全的 stringbuilder是线程不安全的HashTable是线程安全的 HashMap不是线程安全的 2.对象及变量的并发访问下的问题。 方法内的变量因为是方法的私有变量所有不存在线程安全的问题。因此方法内的变量是线程安全的。 多个线程如果同时访问一个对象中的实例变量则该实例变量不是线程安全的。 synchronized synchronized取得的锁都是对象锁哪个线程先执行带synchronized关键字的方法哪个线程就持有该方法所属对象的锁lock那么其他线程只能呈等待状态前提是多个线程访问的是同一个对象。 但如果多个线程访问多个对象则jvm会创建多个锁。 A线程先持有object对象的lock锁B线程可以以异步的方式调用object对象中的非synchronized类型的方法。 A线程先持有object对象的lock锁B线程如果这时调用object对象中的synchronized类型的方法则需等待也就是同步。 synchronized 拥有锁重入的功能。 锁重入自己可以再次获取自己的内部锁例如一条线程获得了某个对象的锁此时这个对象锁还没有释放当其再次想要获取这个对象的锁的时候还是可以获取的如果不可锁重入的话就会造成死锁。 可重入锁也支持在父子类继承的环境中。当存在父子类存在继承关系时子类是完全可通过可重入锁调用父类的同步方法。 当一个线程执行的代码出现异常时其所持有的锁会自动释放。 同步是不可以被继承的。 例如父类synchronized关键字修饰过的方法子类下的该方法是不具备该关键字的。除非子类也自己修饰。 关键字synchronized修饰方法的弊端。 比如A线程调用同步方法执行一个长时间的任务那么B线程则必须等待比较长时间。这种情况下可以使用synchronized同步语句块来解决问题。 synchronized方法是对当前对象进行加锁而synchronized代码块是对某一个对象进行加锁。 当一个线程访问object的一个synchronized 同步代码块时另一个线程仍然可以访问该object对象中的非synchronizedthis同步代码块。 当一个线程访问object的一个synchronizedthis同步代码块时其他线程对同一个object中所有其他synchronizedthis同步代码块的访问即将被阻塞。这说明synchronized使用的对象监视器是一个。 如果一个类中有很多个synchronized方法这时虽然能实现同步但会收到阻塞所以影响运行效率但如果使用同步代码块锁非this对象则synchronized非this代码块中的程序与同步方法是异步的不与其他锁this同步方法争抢this锁则可大大提高运行效率。 java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多数时实例变量及方法的参数使用格式为synchronized非this对象 synchronized非this对象x是将x对象本身作为“对象监视器”因此 1.当多个线程同时执行synchronizedx){}同步代码块时呈同步效果。 2.当其他线程执行x对象中synchronized同步方法时呈同步效果。 3.当其他线程执行x对象方法里面的synchronizedthis代码块时也呈现同步效果。 但需要注意如果其他线程调用不加synchronized关键字的方法时还是异步调用。 静态同步synchronized方法与synchronizedclass代码块 关键字synchronized还恶意家用再static静态方法上这是对当前*.java文件对应的class类进行加锁。 在持有不同的锁的情况下一个是对象锁另外一个是class锁会导致异步运行。class锁会对该类的所有对象实例起作用。 同步synchronizedclass)代码块的作用和synchronized static 方法的作用一样。 这里要注意一个数据类型String的常量池特性 将synchronizedstring)同步块与String 联合使用时要注意常量池带来的一些例外。 例子如下 public class Main {public static void main(String[] args) {Service service new Service();ThreadA anew ThreadA(service);a.setName(A);a.start();ThreadB bnew ThreadB(service);b.setName(B);b.start();}
} public class Service {public static void print(String param) {try {synchronized (param) {while(true) {System.out.println(Thread.currentThread().getName());Thread.sleep(500);} }}catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
} public class ThreadA extends Thread{private Service service;public ThreadA(Service service) {super();this.serviceservice;}public void run() {service.print(AA);}
} public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {super();this.serviceservice;}public void run() {service.print(AA);}
} 运行结果就是无限打印A A A A A A 出现这样的情况就是因为String 的两个值都是AA,两个线程持有相同的锁所以导致线程B并不能运行这就是常量池带来的问题。 因此大多数情况同步synchronized代码块都不使用String作为锁对象而用其他比如 new Object()实例化一个Object对象。 同步方法还有个弊端就是容易造成死循环。假如在某类中有多个synchronized修饰的方法线程a和b分别访问不同的synchronized修饰过的方法当a线程访问的方法中出现了死循环b线程则无法访问另外一个方法因为当前的对象锁并没有释放。 死锁的问题当设计程序时双方互相持有了对方的锁就会造成死锁。原因就是线程互相等待对方释放锁。 以上的synchronized 方法和方法块同样适用于内部类和静态内部类。 要注意一种情况就是在线程运行中锁对象发生了改变。 例如当前有个 String lock123的字符串当synchronizedlock的时候如果在该同步代码块中lock的值发生了改变例如变成了456.那么同时访问改代码块的另外一个线程即可立即访问这就是锁对象发生了改变。 还有一种情况就是synchronizeduser注user 是User的对象那么在某线程执行该代码块的时候改变了user中某一属性的值例如user.setUsername(111),则另一访问该代码块的线程并不会立即获得该代码块的锁对象因此原则就是只要对象不变即使对象的某个属性发生变化运行的结果还是同步。 volatile volatile关键字的主要作用是使变量在多个线程间可见。 关键字volatile的作用是强制从公共堆栈中取得变量的值而不是从线程私有数据栈中取得变量的值。 图2-75代表的是没有用volatile关键字的读取某变量的模式 通过使用volatile关键字强制从公共内存中读区变量的值 volatile最致命的缺点是不支持原子性 volatile与synchronized的比较 1.volatile是线程同步的轻量级实现所以性能要比synchronized要好并且volatile只能修饰变量而synchronized可以修饰方法以及代码块。 2.多线程访问volatile不会发生阻塞而synchronized会出现阻塞 3.volatile能保证数据的可见性但不能保证原子性。而synchronized可以保证原子性也可以间接保证可见性因为它会将私有内存和公公内存中的数据做同步。 4.最后终点重申volatile解决的是变量在多个线程之间的可见性而synchronized解决的是多个线程之间访问资源的同步性。 此处提一下线程安全线程安全包含原子性和可见性java的同步机制都是围绕这两个方面来确保线程安全的。 解释一下volatile非原子的特性 如果修改实力变量中的数据比如i这样一个操作并不是原子操作。也就是非线程安全的。i的操作步骤分解如下 1从内存中取出i的值 2计算i的值 3将i的值写到内存中。 假如第二部计算i的值的时候另外一个线程也修改i的值此时就会出现脏数据。解决的办法其实就是使用synchronized关键字。 下图演示一下volatile时出现非线程安全的原因。 use和assign时多次出现但这一操作并不是原子性也就是在read和load之后如果主内存count变量发生修改之后线程工作内存中的值由于已经加载不会产生对应变化也就是私有内存和公共内存中的变量不同步所以计算出来的结果和预期不一样就会出现非线程安全的问题。 综上所述volatile关键字解决的是变量读时的可见性问题但无法保证原子性对于多个线程访问同一个实例变量还是需要加锁同步。 synchronized代码块其实也有volatile同步的功能。 synchronized不仅可以使多个线程访问同一个资源具有同步性而且它还有句将线程工作内存中的私有变量和与公共内存中的变量同步的功能。 关键字synchronized可以保证在同一时刻只有一个线程可以执行某一个方法或代码块它包含两个特征互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象不一致的状态比如修改了某个对象后另外一个线程即可获得修改后的对象的锁。还可以保证进入同步方法或者同步代码块的每个线程都看到由同一个锁保护之前所有的修改效果。 3.线程间通信的详解 使线程间进行通信后系统之间的交互性会更加强大在大大提高CPU利用率的同时还会使程序员对个线程任务在处理的过程中进行有效的把控与监督。 等待通知机制我们通过waitnotify方法来实现。 要注意的是多个线程共同访问一个变量也是一种通信但那种通信机制不是“等待通知”两个线程完全是主动式地读取一个共享变量在花费读取时间的基础上读到的值是不是想要的并不能完全确定。 在调用wait方法之前线程必须获得该对象的对象级别锁即只能在同步方法或者同步代码块中调用wait方法。在执行wait方法后当前线程释放锁。 方法notify同样要在同步方法或同步块中调用即在调用前线程也必须获得该对象的对象级别锁。 要注意的是在执行notify方法后当前线程不会立即释放该对象锁呈wait状态的线程也不能马上获取该对象锁要等到执行notify方法的线程将程序执行完也就是退出synchronized代码块后当前线程才会释放锁而呈wait状态所在的线程才可以获取该对象锁。当获得了该对象锁的线程运行完毕后它会释放掉该对象锁此时如果该对象没有再次使用notify语句则即使该对象已经空闲其他wait状态的线程由于没有得到该对象的通知还会继续阻塞在wait状态直到这个对象发出一个notify()或者notifyAll(). 一句话总结wait和notify 就是wait使线程停止运行notify使停止的线程继续运行。 notify方法可以随机唤醒等待队列中等待同一共享资源的“一个”线程并使该线程退出等待队列进入可运行状态。 notifyAll()方法使所有在等待队列中等待同一共享资源的“全部”线程从等待状态退出进入可运行状态此时优先级最高的那个线程最先执行但也有可能是随机执行这要取决于JVM虚拟机的实现。 每个锁对象都有两个队列一个是就绪队列一个是阻塞队列。就绪队列存储了将要获得锁的线程阻塞队列存储了被阻塞的线程。一个线程被唤醒后才会进入就绪队列等待cpu的调度反之一个线程被wait后就会进入阻塞队列等待下一次被唤醒。 方法waitlong带一个参数的方法功能室等待某一时间内是否有线程对锁紧型唤醒如果超过这个时间则自动唤醒。 还要注意notify的时候有个通知过早的问题不要在另外一个线程wait之前就notify 这样会导致wait的线程永远也得不到通知。 方法join的使用 很多情况下主线程创建并启动子线程如果子线程中要进行大量的耗时运算主线程往往将早于子线程结束之前结束。这是如果主线程响等待子线程执行完成之后再结束比如子线程处理一个数据主线程要取得这个数据中的值就要用到join方法了。 方法join的作用是等待线程对象销毁。 详细来说是使所属的线程对象x正常执行run方法中的任务而使当前线程z进行无限期的阻塞等待线程x销毁后再继续执行线程z后面的代码。 join具有使线程排队运行的作用有些类似同步的运行效果join与synchronized的区别是join在内部使用wait方法进行等待而synchronized关键字使用的是“对象监视器”原理作为同步。 join也有joinlong的方法是设定等待的时间。joinlong内部是使用waitlong的方法实现的所以有释放锁的特点。 而Thread.sleep(long)方法却不释放锁。 类ThreadLocal的使用 类ThreadLocal主要解决的是每个线程绑定自己的值。 类Threadlocal解决的是变量在不同线程间的隔离性也就是不同线程拥有自己的值不同线程中的值是可以放入Threadlocal类中进行保存的。 在不给ThreadLocal类中的静态变量使用set方法之前用get方法返回的都是null。 可以通过创建一个子类继承ThreadLocal类里面有一个方法initialValue()来设定初始值。 public class ThreadLocalExt extends ThreadLocal { protected Object initialValue() { return 我是默认值第一次get不再为null; } } 使用InheritableThreadLocal类可以让子线程从父线程中取得值。 public class InheritableThreadLocalExt extends InheritableThreadLocal { protected Object initialValue() { return new Date().getTime(); } } 通过继承InheritableThreadLocal类可以使父子线程通过 public static InheritableThreadLocalExt tlnew InheritableThreadLocalExt();得到的tl的值是一致的。 如果想修改子线程的值可以重写以下方法 protected Object childValue(Object parentValue) { return parentValue我在子线程加的; } 使用该InheritableThreadLocal类需要注意的一点就是如果子线程在取得值的同时主线程将InheritableThreadLocal中的值进行更改那么子线程取到的值还是旧值。 Lock的使用 ReentranLock也可以实现等待通知模式利用Condition丢翔。Condition可以实现多路通知功能。也就是在一个Lock对象里面可以创建多个Condition实例线程对象可以注册在指定的Condition中从而可以有选择的进行线程通知在调度线程上更加灵活。 在使用notifynotifyall方法进行通知时被通知的线程却是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现前面介绍过的选择性通知这个功能很重要。 Object类中的wait方法相当于Condition类中的await()方法. Object类中的waitlong方法相当于Condition类中的await(long time, TimeUnit unit)方法。 Object类中的notify()方法相当于Condition类中的signal()方法。 Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。 注意在condition.await()调用之前 要先调用lock.lock() 获得监视器。 如果想单独唤醒部分线程就要使用多个Condition对象了也就是condition对象可以唤醒部分指定线程。 锁Lock分为公平锁与非公平锁。 公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的既先来先得的FIFO先进先出顺序而非公平锁就是一种获取锁的抢占机制是随机获得锁的和公平锁不一样的就是先来的不一定先得到锁这个方式可能造成某些线程一致拿不到锁结果也就是不公平的了。 方法int getHoldCount()的作用是查询当前线程保持此锁定的个数也就是调用lock方法的次数。 方法 intgetQueueLength()的作用是反悔正等待此锁定的线程估计数。转载于:https://www.cnblogs.com/Hennessy-Road/p/6519349.html