Java多线程编程核心技术阅读笔记(对象变量的并发访问)

2.1synchronized同步方法

非线程安全 会在多个线程对同一个对象中的实例变量进行并发访问时发生 产生的后果就是脏读也就是读到的数据其实是被改过的。 而线程安全就是以获得的实例变量的值是经过同步处理的 不会出现脏读现象

2.1.1 方法内的变量为线程安全
public class MethodSelf {
    public static  class ThreadA extends  Thread{
        private MethodSelf methodSelf;
        public ThreadA(MethodSelf methodSelf){
            this.methodSelf = methodSelf;
        }
        @Override
        public void run() {
            methodSelf.addI("a");
        }
    }
    public static  class ThreadB extends  Thread{
        private MethodSelf methodSelf;
        public ThreadB(MethodSelf methodSelf){
            this.methodSelf = methodSelf;
        }
        @Override
        public void run() {
            methodSelf.addI("b");
        }
    }
    public void addI(String username){
        try {
                int num = 0;
                if(username.equals("a")){
                    num = 100;
                    System.out.println("a set over!");
                    Thread.sleep(2000);
                }else{
                    num = 200;
                    System.out.println("b set over!");
                }
            System.out.println(username + "->"+ num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        MethodSelf thread = new MethodSelf();
        ThreadA threadA  = new ThreadA(thread);
        threadA.start();
        ThreadB threadB  = new ThreadB(thread);
        threadB.start();
    }
}

方法内得变量为线程安全
在这里插入图片描述

2.1.2 实例变量非线程安全

如果多个线程同时访问同一个实例变量则可能出现非线程安全问题
将num修改为实例变量再运行程序

   private int num = 0;

在这里插入图片描述

2.1.3 多个对象多个锁
public class MethodSelf {
    private int num = 0;
    public static class ThreadA extends Thread {
        private MethodSelf methodSelf;

        public ThreadA(MethodSelf methodSelf) {
            this.methodSelf = methodSelf;
        }

        @Override
        public void run() {
            methodSelf.addI("a");
        }
    }

    public static class ThreadB extends Thread {
        private MethodSelf methodSelf;

        public ThreadB(MethodSelf methodSelf) {
            this.methodSelf = methodSelf;
        }

        @Override
        public void run() {
            methodSelf.addI("b");
        }
    }

    synchronized   public  void addI(String username) {
        try {

            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + "->" + num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        MethodSelf thread1 = new MethodSelf();
        MethodSelf thread2 = new MethodSelf();
        ThreadA threadA = new ThreadA(thread1);
        threadA.start();
        ThreadB threadB = new ThreadB(thread2);
        threadB.start();
    }
}

上面是两个线程分别访问同一个类的两个不同的实例的同步方法 效果却以异步方式运行
从上面的程序来看 addI 方法使用synchronized关键字但打印的顺序不是同步的 关键字
synchronized取得的锁都是对象锁而不是把一段代码或方法当做锁 所以哪个线程先执行
带synchronized关键字的方法 哪个线程就持有该方法所属对象的锁LOCK 其它线程只能
呈等待状态 前提是多个线程访问的是同一个对象
在这里插入图片描述

2.1.4 synchronized 方法与锁对象
public class LockObject {
    public synchronized void  methodA(){
        try {
            System.out.println("begin" + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static class ThreadA extends  Thread{
        private LockObject object;
        public ThreadA(LockObject object){
            this.object = object;
        }

        @Override
        public void run() {
            super.run();
            object.methodA();
        }
    }

    public static class ThreadB extends  Thread{
        private LockObject object;
        public ThreadB(LockObject object){
            this.object = object;
        }

        @Override
        public void run() {
            super.run();
            object.methodA();
        }
    }
    public static class Run{
        public static void main(String[] args) {
            LockObject object = new LockObject();
            ThreadA a = new ThreadA(object);
            a.setName("A");
            ThreadB b = new ThreadB(object);
            b.setName("B");
            a.start();
            b.start();
        }
    }
}

当A释放锁后B才执行 关键字synchronized 一定是排队运行的 只有共享资源的读写访问才需要同步化 如果不是共享资源那么更本没有同步的必要
在这里插入图片描述
其它方法被调用会是什么效果呢

public class LockObject {
    public synchronized void  methodA(){
        try {
            System.out.println("methodA begin" + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public  void  methodB(){
        try {
            System.out.println("methodB begin" + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static class ThreadA extends  Thread{
        private LockObject object;
        public ThreadA(LockObject object){
            this.object = object;
        }

        @Override
        public void run() {
            super.run();
            object.methodB();
            object.methodA();
        }
    }

    public static class ThreadB extends  Thread{
        private LockObject object;
        public ThreadB(LockObject object){
            this.object = object;
        }

        @Override
        public void run() {
            super.run();
            object.methodA();
        }
    }
    public static class Run{
        public static void main(String[] args) {
            LockObject object = new LockObject();
            ThreadA a = new ThreadA(object);
            a.setName("A");
            ThreadB b = new ThreadB(object);
            b.setName("B");
            a.start();
            b.start();
        }
    }
}

在这里插入图片描述
methodB 方法加上 synchronized 关键字重新运行
在这里插入图片描述
A线程先持有对象的LOCK锁 B线程可以异步调用对象中非synchronized类型方法
A线程先持有对象的LOCK锁 B线程如果在这时调用synchronized类型的方法则需要等待

2.1.5 synchronized 锁重入

synchronized 拥有锁重入的功能 就是在使用synchronized时 当一个线程得到一个对象锁后 再次请求此对象时是可以再次得到该对象的锁

public class SynchronizedGo {
    synchronized public void service1() {
        System.out.println("service1");
        this.service2();
    }

    synchronized public void service2() {
        System.out.println("service2");
        this.service3();
    }

    synchronized public void service3() {
        System.out.println("service3");
    }

    public static class MyThreadA extends Thread{
        @Override
        public void run() {
            SynchronizedGo synchronizedGo = new SynchronizedGo();
            synchronizedGo.service1();
        }
    }

    public static class Run{
        public static void main(String[] args) {
            MyThreadA myThreadA = new MyThreadA();
            myThreadA.start();
        }
    }
}

可重入锁的概念是 自己可以再次获取自己的内部锁 比如有条线程获得某个对象的锁 此时这个锁还没释放 当再次想要获取这个对象的锁的时候 还是可以获取的 如果不可重入的话 会造成死锁 当存在子父类继承关系时 子类是完全可以通过"可重入锁"调用父类的同步方法
在这里插入图片描述

2.1.6 出现异常 锁自动释放
package threadMain.chat2;

public class ThrowLock {
  synchronized   public  void testMethod(){
        if(Thread.currentThread().getName().equals("a")){
            System.out.println(Thread.currentThread().getName());
            int i = 1;
            while (i == 1){
                System.out.println("while");
                Integer.parseInt("a");
            }
        }else{
            System.out.println("B run");
        }
    }
    public static class ThreadA extends Thread{
      private  ThrowLock throwLock;
      public ThreadA(ThrowLock throwLock){
          this.throwLock = throwLock;
      }
        @Override
        public void run() {
            throwLock.testMethod();
        }
    }
    public static class ThreadB extends Thread{
      private  ThrowLock throwLock;
      public ThreadB(ThrowLock throwLock){
          this.throwLock = throwLock;
      }
        @Override
        public void run() {
            throwLock.testMethod();
        }
    }

    public static class Run{
        public static void main(String[] args) {
            ThrowLock throwLock = new ThrowLock();
            ThreadA a = new ThreadA(throwLock);
            a.setName("a");
            a.start();
            ThreadB b = new ThreadB(throwLock);
            b.setName("b");
            b.start();
        }
    }
}

线程A出现异常并释放锁 线程B进入方法正常打印 出现异常的锁被自动释放了
在这里插入图片描述

2.2 synchronized 同步语句块

用关键字synchronized 声明方法在某些情况下是有弊端的 比如A线程调用同步方法执行一个长时间的任务 那么B线程则必须等待比较长的时间 在这种情况可以使用同步块来解决

2.2.1 synchronized 同步块的使用

当两个并发线程访同一个对象Object中的synchronized (this) 一段时间内只能有一个宣线程被执行 另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块

package threadMain.chat2;

public class SynchronizedDemo {
    public void serviceMethod(){
        try {
            synchronized (this){
                System.out.println("begin");
                Thread.sleep(2000);
                System.out.println("end");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static class ThreadA extends Thread{
        private SynchronizedDemo demo;
        public ThreadA(SynchronizedDemo  demo){
            this.demo = demo;
        }

        @Override
        public void run() {
            demo.serviceMethod();
        }
    }
    public static class ThreadB extends Thread{
        private SynchronizedDemo demo;
        public ThreadB(SynchronizedDemo  demo){
            this.demo = demo;
        }

        @Override
        public void run() {
            demo.serviceMethod();
        }
    }

    public static class Run{
        public static void main(String[] args) {
            SynchronizedDemo demo = new SynchronizedDemo();
            ThreadA a = new ThreadA(demo);
            a.setName("a");
            a.start();
            ThreadB b = new ThreadB(demo);
            b.setName("b");
            b.start();
        }

    }
}

2.1.2 用同步代码块解决同步方法弊端
    public  void doLongTimeTask(){
        try {
            System.out.println("begin task");
            Thread.sleep(3000);
            String privateGetData1 =  Thread.currentThread().getName();
            String privateGetData2 = Thread.currentThread().getName();
            synchronized (this){
                getDate1 = privateGetData1;
                getDate1 = privateGetData2;
            }
            System.out.println(getDate1);
            System.out.println(getDate2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
2.1.3 一半同步 一半异步

不在 synchronized 块中就是异步执行, 在 synchronized 块中就是同步执行。

package threadMain.chat2;

public class TaskDemo {
    public void doLongTimeTask() {
        for (int i = 0; i < 100; i++){
            System.out.println(Thread.currentThread().getName() + "i=" +(i + 1));
        }
        System.out.println("");
        synchronized (this){
            for (int i = 0; i < 100; i++){
                System.out.println(Thread.currentThread().getName()S + "i=" +(i + 1));
            }
        }



    }
    public static class MyThreadA extends Thread{
        private TaskDemo taskDemo;
        public MyThreadA(TaskDemo taskDemo){
            this.taskDemo = taskDemo;
        }

        @Override
        public void run() {
            taskDemo.doLongTimeTask();
        }
    }
    public static class MyThreadB extends Thread{
        private TaskDemo taskDemo;
        public MyThreadB(TaskDemo taskDemo){
            this.taskDemo = taskDemo;
        }

        @Override
        public void run() {
            taskDemo.doLongTimeTask();
        }
    }

    public static  class Run{
        public static void main(String[] args) {
            TaskDemo taskDemo = new TaskDemo();
            MyThreadA threadA = new  MyThreadA(taskDemo);
            threadA.start();
            MyThreadB threadB = new  MyThreadB(taskDemo);
            threadB.start();
        }
    }
}

在这里插入图片描述

2.1.4 synchronized 代码块间的同步性

当一个线程访问 object 的一个synchronized(this) 同步代码块时, 其他线程对同一个 object 中所有其他 synchronized(this) 同步代码块的访问将被阻塞, 这说明 synchronized 使用的 “对象监视器” 是一个

package threadMain.chat2;

public class SynchronizedDemo {
    public void serviceMethodA() {
        try {
            synchronized (this) {
                System.out.println("begin");
                Thread.sleep(2000);
                System.out.println("end");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void serviceMethodB() {
        synchronized (this) {
            System.out.println("begin");
            System.out.println("end");
        }
    }

    public static class ThreadA extends Thread {
        private SynchronizedDemo demo;

        public ThreadA(SynchronizedDemo demo) {
            this.demo = demo;
        }

        @Override
        public void run() {
            demo.serviceMethodA();
        }
    }

    public static class ThreadB extends Thread {
        private SynchronizedDemo demo;

        public ThreadB(SynchronizedDemo demo) {
            this.demo = demo;
        }

        @Override
        public void run() {
            demo.serviceMethodB();
        }
    }

    public static class Run {
        public static void main(String[] args) {
            SynchronizedDemo demo = new SynchronizedDemo();
            ThreadA a = new ThreadA(demo);
            a.setName("a");
            a.start();
            ThreadB b = new ThreadB(demo);
            b.start();
            b.setName("b");
        }

    }
}

在这里插入图片描述
两个同步代码块按顺序执行

2.1.5 验证同步 synchronized(this)代码块是锁定当前对象的
package threadMain.chat2;

public class TaskDemo {
    public void otherTask(){
        System.out.println("---------------------");
    }
    public void doLongTimeTask() {
        synchronized (this){
            for (int i = 0; i < 100000; i++){
                System.out.println(Thread.currentThread().getName() + "i=" +(i + 1));
            }
        }



    }
    public static class MyThreadA extends Thread{
        private TaskDemo taskDemo;
        public MyThreadA(TaskDemo taskDemo){
            this.taskDemo = taskDemo;
        }

        @Override
        public void run() {
            taskDemo.doLongTimeTask();
        }
    }
    public static class MyThreadB extends Thread{
        private TaskDemo taskDemo;
        public MyThreadB(TaskDemo taskDemo){
            this.taskDemo = taskDemo;
        }

        @Override
        public void run() {
            taskDemo.otherTask();
        }
    }

    public static  class Run{
        public static void main(String[] args) throws InterruptedException {
            TaskDemo taskDemo = new TaskDemo();
            MyThreadA threadA = new  MyThreadA(taskDemo);
            threadA.start();
            Thread.sleep(100);
            MyThreadB threadB = new  MyThreadB(taskDemo);
            threadB.start();
        }
    }
}

在这里插入图片描述

2.1.6 将任意对象作为对象监视器

多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronized(this)同步代码块时 调用的效果就是按顺序执行 也就是同步的 阻塞的

package threadMain.chat2;

public class Service {
    private String usernameParam;
    private String passwordParam;
    private String anyString = new String();

    public void setUsernamePassword(String username,String password){
        try {
            synchronized (anyString){
                System.out.println("线程" + Thread.currentThread().getName() + "进入同步块");
                usernameParam = username;
                Thread.sleep(3000);
                passwordParam = password;
                System.out.println("线程" + Thread.currentThread().getName() + "离开同步块");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static class ThreadA extends Thread{
        private Service service;
        public ThreadA(Service  service){
            this.service = service;
        }

        @Override
        public void run() {
            service.setUsernamePassword("a","aa");
        }
    }
    public static class ThreadB extends Thread{
        private Service service;
        public ThreadB(Service  service){
            this.service = service;
        }

        @Override
        public void run() {
            service.setUsernamePassword("b","bb");
        }
    }
    public static class Run{
        public static void main(String[] args) {
            Service service = new Service();
            ThreadA threadA = new ThreadA(service);
            threadA.setName("A");
            threadA.start();
            ThreadB threadB = new ThreadB(service);
            threadB.setName("B");
            threadB.start();
        }
    }
}

在这里插入图片描述
锁非this对象具有一定的优点 如果一个类中有很多个synchronized 虽然能实现同步 但会受到阻塞 所以影响运行效率 但如果使用同步代码块锁非this对象则 synchronized(非this)代码块中的程序与同步方法是异步的 不会与其它锁this同步方法争抢this锁 大大提高运行效率


    public void setUsernamePassword(String username,String password){
         String anyString = new String();
        try {
            synchronized (anyString){
                System.out.println("线程" + Thread.currentThread().getName() + "进入同步块");
                usernameParam = username;
                Thread.sleep(3000);
                passwordParam = password;
                System.out.println("线程" + Thread.currentThread().getName() + "离开同步块");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述
不是同步的而是异步 因为不是同一个锁 对象监视器必须是同一个对象 如果不是同一个对象监视器 结果就是异步调用了

package threadMain.chat2;

public class Service {
    private String usernameParam;
    private String passwordParam;

    public void a() {
        String anyString = new String();
        try {
            synchronized (anyString) {
                System.out.println("a begin");
                Thread.sleep(3000);
                System.out.println("a end");
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    synchronized public void b() {
        System.out.println("b begin");
        System.out.println("b end");
    }

    public static class ThreadA extends Thread {
        private Service service;

        public ThreadA(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.a();
        }
    }

    public static class ThreadB extends Thread {
        private Service service;

        public ThreadB(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.b();
        }
    }

    public static class Run {
        public static void main(String[] args) {
            Service service = new Service();
            ThreadA threadA = new ThreadA(service);
            threadA.setName("A");
            threadA.start();
            ThreadB threadB = new ThreadB(service);
            threadB.setName("B");
            threadB.start();
        }
    }
}

在这里插入图片描述
由于对象监视器不同 所以运行结果就是异步的 同步代码块放在非同步synchronized 方法中进行声明 并不能保证调用方法的线程的执行同步/顺序性 也就是线程调用方法的顺序是无序的 虽然在同步块中执行的顺序是同步的 这样极易出现脏读问题

package threadMain.chat2;

import java.util.ArrayList;
import java.util.List;

public class ListDemo {
    private List list = new ArrayList<>();

    synchronized  public void add(String data){
        list.add(data);
    }
    synchronized  public int getSize(){
        return list.size();
    }

    public static class MyService{
        public ListDemo addServiceMethod(ListDemo list,String data){
            try {
                if(list.getSize() < 1){
                    Thread.sleep(2000);
                    list.add(data);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return list;
        }
    }

    public static class ThreadA extends Thread{
        private ListDemo listDemo ;
        public ThreadA(ListDemo listDemo){
            this.listDemo =listDemo;
        }
        @Override
        public void run() {
            MyService myService = new MyService();
            myService.addServiceMethod(listDemo,"a");
        }
    }

    public static class ThreadB extends Thread{
        private ListDemo listDemo ;
        public ThreadB(ListDemo listDemo){
            this.listDemo =listDemo;
        }
        @Override
        public void run() {
            MyService myService = new MyService();
            myService.addServiceMethod(listDemo,"b");
        }
    }
    public static class Run{
        public static void main(String[] args) {
            try {
                ListDemo listDemo = new ListDemo();
                ThreadA threadA = new ThreadA(listDemo);
                threadA.setName("A");
                threadA.start();
                ThreadB threadB = new ThreadB(listDemo);
                threadB.setName("B");
                threadB.start();
                Thread.sleep(6000);
                System.out.println("listsize =" + listDemo.getSize());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

在这里插入图片描述
脏读出现了 出现的原因是两个线程以异步的方式返回List参数的size大小 解决方法就是同步化

2.2.7 细化3个结论

当多个线程调用synchronized 同步代码块时呈同步效果
当其他线程执行X对象中的synchronized同步方法时呈同步效果
当其他线程执行X对象方法里面的synchronized(this)代码块时也呈同步效果
如果其它线程调用不加synchronized 关键字的方法时 还是异步调用

2.2.8 静态同步synchronized 方法

关键字synchronized 还可以应用在static静态方法上 如果这样写就是对当前 *.java文件对应的class类进行持锁

package threadMain.chat2;

public class Service {
    private String usernameParam;
    private String passwordParam;

   synchronized public static void a() {
        String anyString = new String();
        try {
            synchronized (anyString) {
                System.out.println("a begin");
                Thread.sleep(3000);
                System.out.println("a end");
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    synchronized public static void b() {
        System.out.println("b begin");
        System.out.println("b end");
    }

    synchronized public  void c() {
        System.out.println("c begin");
        System.out.println("c end");
    }

    public static class ThreadA extends Thread {
        private Service service;

        public ThreadA(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.a();
        }
    }

    public static class ThreadB extends Thread {
        private Service service;

        public ThreadB(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.b();
        }
    }
    public static class ThreadC extends Thread {
        private Service service;

        public ThreadC(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.c();
        }
    }

    public static class Run {
        public static void main(String[] args) {
            Service service = new Service();
            ThreadA threadA = new ThreadA(service);
            threadA.setName("A");
            threadA.start();
            ThreadB threadB = new ThreadB(service);
            threadB.setName("B");
            threadB.start();
            ThreadC threadC = new ThreadC(service);
            threadC.setName("C");
            threadC.start();
        }
    }
}

异步原因是持有不同的锁 一个是对象锁 另一个是class锁 而class锁可以对类的所有对象实例起作用
在这里插入图片描述

package threadMain.chat2;

public class Service {
    private String usernameParam;
    private String passwordParam;

   synchronized public static void a() {
        String anyString = new String();
        try {
            synchronized (anyString) {
                System.out.println("a begin");
                Thread.sleep(3000);
                System.out.println("a end");
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    synchronized public static void b() {
        System.out.println("b begin");
        System.out.println("b end");
    }


    public static class ThreadA extends Thread {
        private Service service;

        public ThreadA(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.a();
        }
    }

    public static class ThreadB extends Thread {
        private Service service;

        public ThreadB(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.b();
        }
    }


    public static class Run {
        public static void main(String[] args) {
            Service service1 = new Service();
            Service service2 = new Service();
            ThreadA threadA = new ThreadA(service1);
            threadA.setName("A");
            threadA.start();
            ThreadB threadB = new ThreadB(service2);
            threadB.setName("B");
            threadB.start();

        }
    }
}

虽然是不同对象但静态的同步方法还是同步运行 同步synchronized(class)代码块的作用其实和synchronized static方法的作用一样
在这里插入图片描述

2.2.9 数据类型String的常量池特性

在JVM中具有String 常量池缓存的功能 将synchronized(String)同步块与String联合使用时 要注意常量池带来的一些例外

package threadMain.chat2;

public class StringDemo {
    public static void print(String stringParam){
        try {
            synchronized (stringParam){
                while (true){
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(1000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static class ThreadA extends Thread{
        private StringDemo stringDemo;
        public ThreadA(StringDemo stringDemo){
            this.stringDemo = stringDemo;
        }

        @Override
        public void run() {
            stringDemo.print("AA");
        }
    }
    public static class ThreadB extends Thread{
        private StringDemo stringDemo;
        public ThreadB(StringDemo stringDemo){
            this.stringDemo = stringDemo;
        }

        @Override
        public void run() {
            stringDemo.print("AA");
        }
    }

    public static class Run{
        public static void main(String[] args) {
            StringDemo stringDemo = new StringDemo();
            ThreadA threadA = new ThreadA(stringDemo);
            threadA.setName("A");
            threadA.start();
            ThreadB threadB = new ThreadB(stringDemo);
            threadB.setName("B");
            threadB.start();
        }
    }
}

执行结果死循环 因为String的两个值都是AA 两个线程持有相同的锁 所以造成B不能执行 因此在大多数的情况 同步代码块都不使用String作为锁对象 而改用其它 比如实例化一个Object对象 当它并不放入缓存中
在这里插入图片描述

package threadMain.chat2;

import java.util.Objects;

public class StringDemo {
    public static void print(Object object){
        try {
            synchronized (object){
                while (true){
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(1000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static class ThreadA extends Thread{
        private StringDemo stringDemo;
        public ThreadA(StringDemo stringDemo){
            this.stringDemo = stringDemo;
        }

        @Override
        public void run() {
            stringDemo.print(new Object());
        }
    }
    public static class ThreadB extends Thread{
        private StringDemo stringDemo;
        public ThreadB(StringDemo stringDemo){
            this.stringDemo = stringDemo;
        }

        @Override
        public void run() {
            stringDemo.print(new Object());
        }
    }

    public static class Run{
        public static void main(String[] args) {
            StringDemo stringDemo = new StringDemo();
            ThreadA threadA = new ThreadA(stringDemo);
            threadA.setName("A");
            threadA.start();
            ThreadB threadB = new ThreadB(stringDemo);
            threadB.setName("B");
            threadB.start();
        }
    }
}

在这里插入图片描述

2.2.10 同步synchronized 方法无限等待与解决

同步方法容易造成死循环

package threadMain.chat2;

public class Service {
   synchronized public static void a() {
       System.out.println("a begin");
       boolean is = true;
       while (is){
       }
       System.out.println("a end");
    }

    synchronized public static void b() {
        System.out.println("b begin");
        System.out.println("b end");
    }


    public static class ThreadA extends Thread {
        private Service service;

        public ThreadA(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.a();
        }
    }

    public static class ThreadB extends Thread {
        private Service service;

        public ThreadB(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.b();
        }
    }


    public static class Run {
        public static void main(String[] args) {
            Service service1 = new Service();
            Service service2 = new Service();
            ThreadA threadA = new ThreadA(service1);
            threadA.setName("A");
            threadA.start();
            ThreadB threadB = new ThreadB(service2);
            threadB.setName("B");
            threadB.start();

        }
    }
}

线程B 永远得不到运行的机会 锁死了
这时可以用同步块来解这样的问题

package threadMain.chat2;

public class Service {
    static Object object1 = new Object();
    public static void a() {
        synchronized (object1){
            System.out.println("a begin");
            boolean is = true;
            while (is) {
            }
            System.out.println("a end");
        }
    }
    static Object object2 = new Object();
    public static void b() {
        synchronized (object2){
            System.out.println("b begin");
            System.out.println("b end");
        }

    }


    public static class ThreadA extends Thread {
        private Service service;

        public ThreadA(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.a();
        }
    }

    public static class ThreadB extends Thread {
        private Service service;

        public ThreadB(Service service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.b();
        }
    }


    public static class Run {
        public static void main(String[] args) {
            Service service1 = new Service();
            Service service2 = new Service();
            ThreadA threadA = new ThreadA(service1);
            threadA.setName("A");
            threadA.start();
            ThreadB threadB = new ThreadB(service2);
            threadB.setName("B");
            threadB.start();

        }
    }
}

不再出现同步等待情况
在这里插入图片描述

2.3 Volatile 关键字

Volatile 的主要作用是使变量在多个线程间可见

2.3.1 关键字volatile与死循环
package threadMain.chat2;

import com.sun.org.apache.regexp.internal.RE;

public class VolatileDemo {
    private boolean isContinuePrint = true;
    public boolean isContinuePrint(){
        return isContinuePrint;
    }

    public void setContinuePrint(boolean continuePrint) {
        isContinuePrint = continuePrint;
    }
    public void printStringMethod(){
        try {
            while (isContinuePrint == true){
                System.out.println("run " + Thread.currentThread().getName());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static class Run{
        public static void main(String[] args) {
            VolatileDemo volatileDemo = new VolatileDemo();
            volatileDemo.printStringMethod();
            System.out.println("我要停止它" + Thread.currentThread().getName());
            volatileDemo.setContinuePrint(false);
        }
    }
}

main线程一直在处理while()循环 导致程序不能执行后面的代码
在这里插入图片描述

2.3.2 解决同步死循环
package threadMain.chat2;

import com.sun.org.apache.regexp.internal.RE;

public class VolatileDemo implements Runnable{
    private boolean isContinuePrint = true;
    public boolean isContinuePrint(){
        return isContinuePrint;
    }

    public void setContinuePrint(boolean continuePrint) {
        this.isContinuePrint = continuePrint;
    }
    public void printStringMethod(){
        try {
            while (isContinuePrint == true){
                System.out.println("run " + Thread.currentThread().getName());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        printStringMethod();
    }

    public static class Run{
        public static void main(String[] args) {
            VolatileDemo volatileDemo = new VolatileDemo();
            new Thread(volatileDemo).start();
            System.out.println("我要停止它" + Thread.currentThread().getName());
            volatileDemo.setContinuePrint(false);
        }
    }
}

在这里插入图片描述

2.3.3. 解决异步死循环
package threadMain.chat2;

import com.sun.org.apache.regexp.internal.RE;

public class VolatileDemo extends Thread{
    private boolean isContinuePrint = true;
    public boolean isContinuePrint(){
        return isContinuePrint;
    }

    public void setContinuePrint(boolean continuePrint) {
        this.isContinuePrint = continuePrint;
    }
    public void printStringMethod(){
        try {
            while (isContinuePrint == true){
                System.out.println("run " + Thread.currentThread().getName());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        System.out.println("进入Run了");
        while (isContinuePrint == true){

        }
        System.out.println("线程被停止了");
    }

    public static class Run{
        public static void main(String[] args) {
            try {
                VolatileDemo volatileDemo = new VolatileDemo();
                volatileDemo.start();
                Thread.sleep(1000);
                volatileDemo.setContinuePrint(false);
                System.out.println("已经赋值为false");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

已经赋值为false但还是死循环 因为变量isContinuePrint 存在公共堆栈及线程的私有堆栈中 在JVM被设置-server 模式时为线程运行的效率 线程一直在私有堆栈中取得isContinuePrint 值是ture 代码 volatileDemo.setContinuePrint(false)虽然被执行 更新的却是公共堆栈中的isContinuePrint变量值所以一直为死循环状态
在这里插入图片描述

这个问题其实就是私有堆栈中的值和公共堆栈中的值不同步造成的。 解决这样的问题就
要使用 volatile 关键字了 它主要的作用就是当线程访问 isRimning 这个变量时 强制性从公
共堆栈中进行取值

  private volatile boolean isContinuePrint = true;

通过使用 volatile 关键字 强制的从公共内存中读取变量的值 使用 volatile 关键字增加了实例变量在多个线程之间的可见性 但 volatile 关键字最致命的缺点是不支持原子性

synchronized 和 volatile的区别
1 关键字 volatile 是线程 M 步的轻量级实现 所以 volatile 性能肯定比 synchronized 要好 并且 volatile 只能修饰于变量 而 synchronized 可以修饰方法 以及代码块
2 多线程访问 volatile 不会发生阻塞 而 synchronized 会出现阻塞
3 volatile 能保证数据的可见性, 但不能保证原子性; 而 synchronized 可以保证原子性也可以间接保证可见性
4 关键字 volatile 解决的是变量在多个线程之间的可见性 而 synchronized关键字解决的是多个线程之间访问资源的同步性。

2.3.4 volatile非原子的特性
package threadMain.chat2;

public class VolatileTe extends Thread {
    volatile  public static int count;
    private static void addCount(){
        for (int i = 0; i < 100; i ++){
            count++;
        }
        System.out.println("count=" + count);
    }

    @Override
    public void run() {
        addCount();
    }

    public static class Run{
        public static void main(String[] args) {
            VolatileTe[] volatileTes = new VolatileTe[100];
            for (int i = 0; i < 100; i ++){
                volatileTes[i] = new VolatileTe();
            }
            for (int i = 0; i < 100; i ++){
                volatileTes[i].start();
            }
        }
    }
}

运行结果不是10000
在这里插入图片描述

加上synchronized

synchronized private static void addCount() {
        for (int i = 0; i < 100; i++) {
            count++;
        }
        System.out.println("count=" + count);
    }

在这里插入图片描述
如果在方法 private static void addCount()前加人 synchronized 同步关键字, 也就没有必要再使用 volatile 关键字来声明 count 变量了关键字 volatile 主要使用的场合是在多个线程中可以感知实例变量被更改了 并且可以获得最新的值使用 也就是用多线程读取共享变量时可以获得最新值使用 所以说 volatile 本身并不处理数据的原子性, 而是强制对数据的读写及时影响到主内存的

2.3.5 synchronized 代码块有 volatile 同步的功能
package threadMain.chat2;


public class VolatileDemo extends Thread{
    private  boolean isContinuePrint = true;
    public void printStringMethod(){
            while (isContinuePrint == true){

            }
            System.out.println("停下来了");

    }
    public void stopMethod(){
        isContinuePrint = false;
    }

    public static  class ThreadA extends Thread{
        private VolatileDemo volatileDemo;
        public ThreadA(VolatileDemo volatileDemo){
            this.volatileDemo = volatileDemo;
        }

        @Override
        public void run() {
            volatileDemo.printStringMethod();
        }
    }
    public static  class ThreadB extends Thread{
        private VolatileDemo volatileDemo;
        public ThreadB(VolatileDemo volatileDemo){
            this.volatileDemo = volatileDemo;
        }

        @Override
        public void run() {
            volatileDemo.stopMethod();
        }
    }

    public static class Run{
        public static void main(String[] args) {
            try {
                VolatileDemo volatileDemo = new VolatileDemo();
                ThreadA a = new ThreadA(volatileDemo);
                a.start();
                Thread.sleep(1000);
                ThreadB b = new ThreadB(volatileDemo);
                b.start();
                System.out.println("发起停止命令");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

已经发起停止命令但还是死循环 这就i是各个线程间的数据值没有可视性造成的 而关键字 synchronized 可以具有可视性。
在这里插入图片描述

    public void printStringMethod(){
        String str = new String();
            while (isContinuePrint == true){
                synchronized (str){

                }
            }
            System.out.println("停下来了");

    }

关键字 synchronized 可以保证在同一时刻, 只有一个线程可以执行某一个方法或某一个代码块。 它包含两个特征: 互斥性和可见性。 同步 synchronized 不仅可以解决一个线程看到对象处于不一致的状态, 还可以保证进入同步方法或者同步代码块的每个线程, 都看到由同一个锁保护之前所有的修改效果。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值