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的对象实例的锁——对象锁。
java线程同步以及对象锁和类锁解析(多线程synchronized关键字)的更多相关文章
- (删)Java线程同步实现二:Lock锁和Condition
在上篇文章(3.Java多线程总结系列:Java的线程同步实现)中,我们介绍了用synchronized关键字实现线程同步.但在Java中还有一种方式可以实现线程同步,那就是Lock锁. 一.同步锁 ...
- 【Thread】java线程之对象锁、类锁、线程安全
说明: 1.个人技术也不咋滴.也没在项目中写过线程,以下全是根据自己的理解写的.所以,仅供参考及希望指出不同的观点. 2.其实想把代码的github贴出来,但还是推荐在初学的您多亲自写一下,就没贴出来 ...
- Java锁Synchronized,对象锁和类锁举例
Java的锁分为对象锁和类锁. 1. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内针对该对象的操作只能有一个线程得到执行.另一个线程必须 ...
- Java锁Synchronized对象锁和类锁区别
java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁.线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁.获得内置锁的唯一途径就是进入这个锁的保 ...
- Java对象锁和类锁全面解析(多线程synchronized关键字)
最近工作有用到一些多线程的东西,之前吧,有用到synchronized同步块,不过是别人怎么用就跟着用,并没有搞清楚锁的概念.最近也是遇到一些问题,不搞清楚锁的概念,很容易碰壁,甚至有些时候自己连用没 ...
- java的对象锁和类锁
在java编程中,经常需要用到同步,而用得最多的也许是synchronized关键字了,下面看看这个关键字的用法. 因为synchronized关键字涉及到锁的概念,所以先来了解一些相关的锁知识. j ...
- java 对象锁和类锁的区别(转)
java 对象锁和类锁的区别 转自; ) ); ; ) ); 上述的代码,第一个方法时用了同步代码块的方式进行同步,传入的对象实例是this,表明是当前对象,当然,如果需要同步其他对象实例,也不可 ...
- Java 中对象锁和类锁的区别? 关键字 Synchronized的用法?
一 对象锁和类锁的关系 /* * 对象锁和[类锁] 全局锁的关系? 对象锁是用于对象实例方法,或者一个对象实例上的 this 类锁是用于类的静态方法或者一个类的class对象上的. Ag.class ...
- Java线程池的原理及几类线程池的介绍
刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...
随机推荐
- PHP中针对区域语言标记信息的操作
相信大家对 zh_CN 这个东西绝对不会陌生,不管是 PHP 中,还是在我们的网页上,都会见到它的身影.其实这就是指定我们的显示编码是什么国家或者地区的,使用何种语言.对于这种区域语言的标记来说,PH ...
- symfony2中mysql和mongodb的增删改查总结
https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manip ...
- Appium Capability使用进阶
- [转载]linux上用PHP读取WORD文档
在linux上用PHP读取WORD文档,其实是使用了 antiword程序把word文档转化为txt文档. 再使用php执行系统命令调用而已. 具体操作如下: 1.安装antiword 官方站:htt ...
- maven插件maven-install-plugin
maven-install-plugin负责将获取的jar包安装到本地仓库 <build> <plugin> <groupId>org.apache.maven.p ...
- Spring Bean装配笔记
Spring Bean装配笔记 Spring中的Bean是一个很重要的概念.Spring作为一个Bean容器,它可以管理对象和对象之间的依赖关系,我们不需要自己建立对象,把这部分工作全部转交给容器完成 ...
- linux 安装libreOffice
linux 安装libreOffice 第一种方式:通过yum install libreoffice* 安装,但在使用docx文档转化为pdf的过程中,发现有些表格样式出现变形,因此采用如下方式安装 ...
- 题解 2020.10.24 考试 T3 数列
题目传送门 题目大意 给出一个数 \(n\),你要构造一个数列,满足里面每个数都是 \(n\) 的因子,且每一个数与前面不互质的个数不超过 \(1\).问有多少种合法方案. 保证 \(n\) 的不同质 ...
- PTA实验7-2-3 求矩阵的局部极大值 (15分)
实验7-2-3 求矩阵的局部极大值 (15分) 给定M行N列的整数矩阵A,如果A的非边界元素A[i][j]大于相邻的上下左右4个元素,那么就称元素A[i][j]是矩阵的局部极大值.本题要求给定矩阵的全 ...
- Linux命令(二)
1.cd命令 这是一个非常基本,也是大家经常需要使用的命令,它用于切换当前目录,它的参数是要切换到的目录的路径,可以是绝对路径,也可以是相对路径.如: cd /root/Docements # 切换到 ...