多线程(三) 同步synchronized
五、同步
1.锁
多线程程序一般是为了完成一些相同的工作而存在的,因此有时间也会共享一些资源,例如对象、变量等等,此时如果不对各个线程进行资源协调,就会出现一些冲突,从而导致程序功能失效。例如下面的示例中的计数器:
public class Sync extends Thread{
public int id;
int count=1000;
static int data=0;
public Sync(int id)
{
this.id=id;
}
public void run()
{
int d=((id%2)==0?1:-1);
for (int i=0;i<count;i++)
{
data+=d;
}
}
public static void main(String []args)
{
Thread A=new Sync(1);
Thread B=new Sync(2);
A.start();
B.start();
try {
Thread.sleep(30000); //main线程等一下Sync线程
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Sync.data);
}
}
上面代码很简单,就是两个线程共同计数,但是线程A加1,线程B减一,各做1000次,最终的结果应该是0,实际运行起来也是0。
接下来,我们将count的值改为10000000,再次运行,三次结果分别为-26137、-9578836、899,其实意思就是结果不固定而且不稳定。
此时我们必须进行一些处理使结果变得正确,即建立同步机制。JVM通过给对象加锁的方法来实现多线程的同步处理,老调重弹一下,对象分为类对象和实例对象,实例对象不用多说,就是new出来的那种,而类对象需要通过forName(String )来获得,如:Class t=Class.forName("java.lang.Thread"); 其中返回值t记为Thread的类对象,一个类的静态成员于和成员方法属于类对象,而不是实例对象。
每个对象有一把锁(lock)和一个等候集(wait set),在一个对象内部,锁住的是同步方法和同步语句块,如下图所示:

那么将方法或者代码块变成同步的方法就是synchronized关键字。
2.synchronized关键字
synchronized关键字主要有代码块和函数两种修饰方法,修饰函数时只需要直接加上关键字即可,修饰代码块时基本格式如下:
synchronized (obj)
{
代码块
}
其中obj代表实例对象或者类对象。
在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。我们改造一下上面的例子,使其成为Runnable接口的形式。
/* Multi-thread Conflict - count minus two million
* wym
* */
public class Sync2 implements Runnable{
public int id;
int count=1000000;
static int data=0;
public boolean b;
public Sync2(int id , boolean b)
{
this.id=id;
this.b=b;
}
public void run()
{
int d=((id%2==0)?1:-1);
for (int i = 0; i < count; i++) {
data += d;
} b = true;
System.out.println("Thread " + id + " is over"); }
public boolean getb()
{
return b;
} public static void main(String []args)
{
Sync2 s=new Sync2(1,false);
Thread t1=new Thread(s,"t1");
Thread t2=new Thread(s,"t2");
t1.start();
t2.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("end"+Sync2.data); }
}
显然还是会产生冲突,因为只是从线程的继承Thread类方式变成了实现Runnable接口,运行一次的结果是-1006544(不固定),但是当我们对run()方法里面加上synchronized关键字之后:
public void run()
{
synchronized (this) {
int d=((id%2==0)?1:-1);
for (int i = 0; i < count; i++) {
data += d;
} b = true;
System.out.println("Thread " + id + " is over");
} }
显然解决了冲突问题,无论运行多少次,结果都是-2000000。那么问题来了,为什么不能继承Thread类来实现呢?前面说到过了,同步锁是基于对象的,继承Thread类时,两个线程需要两个实例对象,而Runnable接口两个线程基于同一个实现该接口的对象的,因此要使用Runnable接口方式。
我们是否能实现基于Thread的方法呢?当然可以!
class M
{
static int z=0;
static int count=1000000;
public void count(int d)
{
synchronized (this) {
for (int i = 0; i < count; i++) {
z += d;
}
} }
}
public class Sync3 extends Thread {
M m;
int id; public Sync3(int id ,M m) {
this.id = id;
this.m=m;
} public int getid() {
return id;
} public void run() {
m.count(id);
} public static void main(String[] args) {
M m1=new M();
Thread t1 = new Sync3(1,m1);
Thread t2 = new Sync3(-1,m1);
t1.start();
t2.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("end"+M.z); }
}
基本思路是在线程类(继承Thread)中添加一个对象的成员域,然后两个线程的这个成员被同一个实例对象赋值,然后将冲突的方法放在该类的方法中,并将这个方法设置同步方法,即可解决第一节中的冲突,输出结果0.
如下两句话:
当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
class M
{
public synchronized void syn(int id)
{
for (int i=0;i<5;i++)
{
System.out.println("Thread "+id+" syn "+i);
}
}
public void nonsyn(int id2)
{
for (int i=0;i<5;i++)
{
System.out.println("Thread "+id2+" nonsyn "+i);
}
} }
public class Sync3 extends Thread {
M m;
int id; public Sync3(int id ,M m) {
this.id = id;
this.m=m;
} public int getid() {
return id;
} public void run() {
if(id==1)
m.syn(id);
else
m.nonsyn(id);
} public static void main(String[] args) {
M m1=new M();
Thread t1 = new Sync3(1,m1);
Thread t2 = new Sync3(2,m1);
t1.start();
t2.start();
}
}
Thread 2 nonsyn 0
Thread 1 syn 0
Thread 2 nonsyn 1
Thread 1 syn 1
Thread 2 nonsyn 2
Thread 2 nonsyn 3
Thread 2 nonsyn 4
Thread 1 syn 2
Thread 1 syn 3
Thread 1 syn 4
其实好几次都是按照顺序来的,好不容易跑出这结果
多线程(三) 同步synchronized的更多相关文章
- Java 多线程(Thread) 同步(synchronized) 以及 wait, notify 相关 [实例介绍]
场景描述 有一家很大的商场,在某市有几个商品配送中心,并有几家分店,这家商场经营很多的商品,平时运营情况是这样的: 根据各分店的商品销售情况,给分店配送相应需求量的商品:并上架到分店指让的位置,供客户 ...
- java多线程(三)——锁机制synchronized(同步语句块)
用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解 ...
- java——多线程的实现方式、三种办法解决线程赛跑、多线程数据同步(synchronized)、死锁
多线程的实现方式:demo1.demo2 demo1:继承Thread类,重写run()方法 package thread_test; public class ThreadDemo1 extends ...
- Java多线程:线程同步与关键字synchronized
一.同步的特性1. 不必同步类中所有的方法, 类可以同时拥有同步和非同步方法.2. 如果线程拥有同步和非同步方法, 则非同步方法可以被多个线程自由访问而不受锁的限制. 参见实验1:http://blo ...
- Java多线程-同步:synchronized 和线程通信:生产者消费者模式
大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...
- Java多线程同步 synchronized 关键字的使用
代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D)运行完这个方法后再运行此线程A, ...
- Java多线程(三)—— synchronized关键字详解
一.多线程的同步 1.为什么要引入同步机制 在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源.必须对这种潜在资源冲突进行预防. 解决方法:在线程使用一个资源时为其加锁即可. 访问资 ...
- “全栈2019”Java多线程第十六章:同步synchronized关键字详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Java自学-多线程 同步synchronized
Java 多线程同步 synchronized 多线程的同步问题指的是多个线程同时修改一个数据的时候,可能导致的问题 多线程的问题,又叫Concurrency 问题 步骤 1 : 演示同步问题 假设盖 ...
随机推荐
- CentOS6.5升级手动安装GCC4.8.2 转载
一.简易安装 操作环境 CentOS6.5 64bit,原版本4.4.7,不能支持C++11的特性~,希望升级到4.8.2 不能通过yum的方法升级,需要自己手动下载安装包并编译 1.1 获取安装包并 ...
- Django-ORM外键属性总结
ForeignKey ForeignKey(ForeignObject) # ForeignObject(RelatedField) to, # 要进行关联的表名 to_field=None, # 要 ...
- 121. 买卖股票的最佳时机( Best Time to Buy and Sell Stock)
题目地址:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/ 解题思路一:暴力求解法 根据题目我们可以知道,我们知道最大 ...
- meta viewport移动端自适应
参考链接:https://www.jianshu.com/p/561357d7cd7b
- 用grok拆分java日志
1.假设一行日志内容如下: [root@VM_0_92_centos opt]# cat error.log -- ::,[ERROR ajp-nio--exec-](cn.com.al1.compo ...
- python控制流 -- if、for、while、range()、sys.exit()
1.布尔值 “布尔”数据类型只有两种:True和False #首字母以T或F开头,后面小写,且不能作为变量赋值 2.比较操作符 == 等于 != 不等于 < 小于 > 大于 &l ...
- python中迭代器和生成器的详细解释
https://www.cnblogs.com/wilber2013/p/4652531.html
- 最好用linux版QQ
这个版本的qq是见过linux下做好用的qq,希望对大家有用; 安装简单,qq易用,不卡死,可以接收文件. 安装过程如下: git clone https://gitee.com/wszqkzqk/d ...
- Java Web开发技术教程入门-自定义标签
回顾: 昨天了解了JSP开发的两种模式Model1和Model2模式.Model1采用JSP+JavaBean技术开发Web应用,它比较适合小规模应用的开发,效率较高,易于实现.但由于在Model1中 ...
- WPF中Matrix介绍
最近在做一些图形变换操作的功能,图形变换涉及大学中的矩阵运算部分的知识,又重新复习了一下矩阵.这里做一下记录.由于不知道矩阵如何输入,一个个截图又麻烦,所以这里就全部用截图了^-^.