• java线程同步以及对象锁和类锁解析(多线程synchronized关键字)


    一、关于线程安全

    1.是什么决定的线程安全问题?
      线程安全问题基本是由全局变量静态变量引起的。
      若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

    2.可以解决多线程并发访问资源的方法有哪些?
      主要有三种方式:分别是同步代码块 、同步方法和锁机制(Lock)
      其中同步代码块和同步方法是通过关键字synchronized实现线程同步

    本文主要是将synchronized关键字用法作为例子来去解释Java中的对象锁和类锁

    二、synchronized关键字各种用法与实例

      事实上,synchronized修饰非静态方法、同步代码块的synchronized (this)用法和synchronized (非this对象)的用法锁的是对象,线程想要执行对应同步代码,需要获得对象锁。

    synchronized修饰静态方法以及同步代码块的synchronized (类.class)用法锁的是类,线程想要执行对应同步代码,需要获得类锁。

    因此,事实上synchronized关键字可以细分为上面描述的五种用法。
    1.同步块synchronized (this)

    public class Ticket1 extends Thread{
        
        private int nums = 0; //出票数
        private int count =20; //剩余
    
        @Override
        public void run() {
            while (true) {
                synchronized (this) {
                    if(count <= 0) {
                        break;
                    }
                    nums++;
                    count--;
                    try {
                        Thread.sleep(550);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("显示出票信息:"+Thread.currentThread().getName()+
                    "抢到第"+nums+"张票,剩余"+count+"张");
                }
            }
        }
        
    
        public static void main(String[] args) {
            Ticket1 ticket1 = new Ticket1();
            Thread anni = new Thread(ticket1,"安妮");
            Thread jack = new Thread(ticket1,"jack");
            anni.start();
            jack.start();
    
        }
    
    }

    这样是同步的,线程获取的是同步块synchronized (this)括号()里面的对象实例的对象锁,这里就是Ticket1 实例对象的对象锁了。

    需要注意的是synchronized (){}的{}前后的代码依旧是异步的

    2.synchronized (非this对象)的用法锁的是对象

    例1:

    public class Ticket2 implements Runnable{
    
    	private int nums = 0; //出票数
    	private int count =25; //剩余
    	//实现线程安全三种方法
    	private final Object lock = new Object();//1.使用私有不变对象锁,使得攻击者无法获取到锁对象(推荐使用)
    	
    	@Override
    	public void run() {
    		while (true) {
    			//2.this 使用对象自身的锁(隐式锁)--对象锁
    			//3.Ticket2.class 给Ticket2加锁 --类锁->使用说明:静态方法则一定会同步,非静态方
    			//法需在单例模式才生效(本例为单例),但是也不能都用静态同步方法,总之用得不好可能会给性能带来极大的影响。
    			synchronized (lock) {
    				if(count <= 0) {
    					break;
    				}
    				nums++;
    				count--;
    				try {
    					Thread.sleep(750);
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    				System.out.println("显示出票信息:"+Thread.currentThread().getName()+
    				"抢到第"+nums+"张票,剩余"+count+"张");
    			}
    		}
    	}
    	
    	public static void main(String[] args) {
    		Ticket2 ticket2 = new Ticket2();
    		Thread zhangsan = new Thread(ticket2,"张三");
    		Thread zhaoyun = new Thread(ticket2,"赵云");
    		zhangsan.start();
    		zhaoyun.start();
    	}
    
    }
    

    例2:

    public class Run2 {
    
        public static void main(String[] args) {
    
            Service service = new Service("xiaobaoge");
    
            ThreadA2 a = new ThreadA2(service);
            a.setName("A");
            a.start();
    
            ThreadB2 b = new ThreadB2(service);
            b.setName("B");
            b.start();
    
        }
    
    }
    
    class Service {
    
        String anyString = new String();
    
        public Service(String anyString){
            this.anyString = anyString;
        }
    
        public void setUsernamePassword(String username, String password) {
            try {
                synchronized (anyString) {
                    System.out.println("线程名称为:" + Thread.currentThread().getName()
                            + "在" + System.currentTimeMillis() + "进入同步块");
                    Thread.sleep(3000);
                    System.out.println("线程名称为:" + Thread.currentThread().getName()
                            + "在" + System.currentTimeMillis() + "离开同步块");
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
    }
    
    class ThreadA2 extends Thread {
        private Service service;
    
        public ThreadA2(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.setUsernamePassword("a", "aa");
    
        }
    
    }
    
    
    class ThreadB2 extends Thread {
    
        private Service service;
    
        public ThreadB2(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            service.setUsernamePassword("b", "bb");
    
        }
    
    }
    

    3.synchronized (class)

    public class Run {
    
        public static void main(String[] args) {
    
            ThreadA a = new ThreadA();
            a.setName("A");
            a.start();
    
            ThreadB b = new ThreadB();
            b.setName("B");
            b.start();
    
        }
    
    }
    class Service {
    
        public static void printA() {
            synchronized (Service.class) {
                try {
                    System.out.println("线程名称为:" + Thread.currentThread().getName()
                            + "在" + System.currentTimeMillis() + "进入printA");
                    Thread.sleep(3000);
                    System.out.println("线程名称为:" + Thread.currentThread().getName()
                            + "在" + System.currentTimeMillis() + "离开printA");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
        public static void printB() {
            synchronized (Service.class) {
                System.out.println("线程名称为:" + Thread.currentThread().getName()
                        + "在" + System.currentTimeMillis() + "进入printB");
                System.out.println("线程名称为:" + Thread.currentThread().getName()
                        + "在" + System.currentTimeMillis() + "离开printB");
            }
        }
    }
    

    4.静态synchronized同步方法  

    public class Run {
    
        public static void main(String[] args) {
    
            ThreadA a = new ThreadA();
            a.setName("A");
            a.start();
    
            ThreadB b = new ThreadB();
            b.setName("B");
            b.start();
    
        }
    
    }
    
    class Service {
    
        synchronized public static void printA() {
            try {
                System.out.println("线程名称为:" + Thread.currentThread().getName()
                        + "在" + System.currentTimeMillis() + "进入printA");
                Thread.sleep(3000);
                System.out.println("线程名称为:" + Thread.currentThread().getName()
                        + "在" + System.currentTimeMillis() + "离开printA");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        synchronized public static void printB() {
            System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
                    + System.currentTimeMillis() + "进入printB");
            System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
                    + System.currentTimeMillis() + "离开printB");
        }
    
    }
    
    
    class ThreadA extends Thread {
        @Override
        public void run() {
            Service.printA();
        }
    
    }
    
    
    class ThreadB extends Thread {
        @Override
        public void run() {
            Service.printB();
        }
    }
    

    5.synchronized修饰非静态方法

    public class Run {
    
        public static void main(String[] args) {
    
            HasSelfPrivateNum numRef = new HasSelfPrivateNum();
    
            ThreadA athread = new ThreadA(numRef);
            athread.start();
    
            ThreadB bthread = new ThreadB(numRef);
            bthread.start();
    
        }
    
    }
    class HasSelfPrivateNum {
    
        private int num = 0;
    
        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=" + num);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    class ThreadA extends Thread {
    
        private HasSelfPrivateNum numRef;
    
        public ThreadA(HasSelfPrivateNum numRef) {
            super();
            this.numRef = numRef;
        }
    
        @Override
        public void run() {
            super.run();
            numRef.addI("a");
        }
    
    }
    class ThreadB extends Thread {
    
        private HasSelfPrivateNum numRef;
    
        public ThreadB(HasSelfPrivateNum numRef) {
            super();
            this.numRef = numRef;
        }
    
        @Override
        public void run() {
            super.run();
            numRef.addI("b");
        }
    
    }
    

    实验结论:两个线程访问同一个对象中的同步方法是一定是线程安全的。本实现由于是同步访问,所以先打印出a,然后打印出b

    这里线程获取的是HasSelfPrivateNum的对象实例的锁——对象锁。

     

  • 相关阅读:
    Python 之 __new__() 方法与实例化
    Nvidia GPU架构演变
    图像增强之各种算子
    傅里叶变换 高通滤波 低通滤波
    SqlServer 把数据库表结构导出为Excel
    java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ 解决方案
    Navicat Premium 12中文破解版 32/64位 v12.1.22 已激活版
    windows下安装mysql-8.0.18-winx64的教程(图文详解)
    SQL Server中的分页查询 select top
    SQL server分页的四种方法(算很全面了)
  • 原文地址:https://www.cnblogs.com/zhusf/p/10566293.html
Copyright © 2020-2023  润新知