多线程(三) 同步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 : 演示同步问题 假设盖 ...
随机推荐
- laravel 5.8 实现消息推送
以下教程是基于5.6 的,在使用5.8实现时遇到一些问题,做一下记录 在我看来,实时通信才是 APP 应用的将来. Socket 服务通常不是那么容易实现,但是 Laravel Echo 服务改变了这 ...
- 【Spring Cloud】Spring Cloud使用总结
项目概要 项目环境信息 IDEA ultimate 2018.3.2 springboot 2.1.7.RELEASE springCloud Greenwich.SR2 Eureka 介绍 基于ne ...
- JDK下bin文件夹常见几个可执行文件
bin目录下有很多的可执行文件: java.exe:运行Java程序,就是启动JVM,让JVM执行指定的编译后的代码: javac.exe:Java的编译器,它用于把Java源码文件(以.java后缀 ...
- git 添加本地项目到远程仓库 记录一下命令
1.初始化 git init 2.关联远程仓库 git remote add origin 你的仓库地址 3.加入到本地仓库 git add * 4.推送(强推).如果不想强推 ,可以先执行下 git ...
- Spring IOC 和Aspectj AOP
1.Aspectj AOP 是一套独立的AOP 解决方案,不仅限于java应用,不依赖其他方案,属于编译时增强,有自己单独的编译器.Spring AOP 是基于Spring 容器的的AOP解决方式,属 ...
- PHP空对象 空数组
PHP定义空对象:$obj = (object)null;或$obj = (object)array(); 定义空数组:$arr = array();或$arr = [];//自 5.4 起可以使用短 ...
- Souvenirs CodeForces - 765F (好题)
大意: 给定序列$a$, $m$个询问$[l,r]$, 回答$[l,r]$内最接近的两个数的差. 考虑离线, 枚举右端点, 每个点维护左端点的贡献, 对于新添一个点$a_r$, 只考虑左侧点比$a_r ...
- WPF使用资源图片
一.加载本项目的图片 WPF引入了统一资源表示Uri来标识和访问资源.其中较为常见的情况是用Uri加载图像.Uri表达式的一把形式为:协议+授权+路径 协议:pack:// 授权:有两种,一种用于访问 ...
- 入门&常量&变量
位:二进制中,每个0或1就是一个位,叫做bit(比特) 字节:计算机最小是存储单元(byte或B) 8bit = 1B 常用cmd命令: 启动: Win+R,输入cmd回车切换盘符 盘符名称:进入文件 ...
- 微信小程序wx.showActionSheet调用客服信息功能
微信小程序wx.showActionSheet调用客服消息功能 官方文档的代码: wx.showActionSheet({ itemList: ['A', 'B', 'C'], success (re ...