多线程(三) 同步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 : 演示同步问题 假设盖 ...
随机推荐
- UOJ#152盘子序列
题面君 那这是一题比较标准的单调栈的题目,维护一下单调栈并访问就好了 int n;//因为我写了十几行头文件..头文件就删了,大家自己加一下吧.. ]; ],s2[],t1,t2; int get() ...
- Java通过字节分割字符串
一.题目描述: 一道Java笔试题.将字符串按给定的字节数进行分割,输出分割后的字符串.要求汉字不能进行拆分,如“a中国”不能拆分成“a+中的一半”. 二.解题思路: 首先利用String类的subs ...
- 【AMAD】newspaper -- 爬取/提取新闻网页中的文本,元数据
动机 简介 用法 源码分析 个人评分 动机 新闻网页,结构大多是类似的. 所以,能不能用一种通用的爬取方法来提取其中的数据? 简介 Newspapaer1受到requests那种简单性API的启发,通 ...
- Python报错module 'scipy.misc' has no attribute 'xxx'
Python报错module 'scipy.misc' has no attribute 'imresize' 解决办法: 安装Pillow包,命令如下: pip install Pillow 然后重 ...
- 【C/C++】assert()函数用法总结
assert()函数用法总结 assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义: #include <assert.h> ...
- flask_script
Flask Script扩展提供向Flask插入外部脚本的功能,包括运行一个开发用的服务器,一个定制的Python shell,设置数据库的脚本,cronjobs,及其他运行在web应用之外的命令行任 ...
- @Transient注解的使用(不被序列化和作为临时变量存储)
转自:https://blog.csdn.net/sinat_29581293/article/details/51810805 java 的transient关键字的作用是需要实现Serilizab ...
- 2019icpc南昌邀请赛B Polynomial (拉格朗日插值法)
题目链接:https://nanti.jisuanke.com/t/40254 题意: 思路: 这题要用到拉格朗日插值法,网上查了一下,找到一份讲得特别好的: -------------------- ...
- Vim命令使用
终端输入vim命令(不区分大小写)进入Vim,起始默认进去是normal模式(即普通模式),使用:q可以退出Vim,使用i(insert)进入编辑模式,开始输入文字,使用Esc键又可以回到normal ...
- codeforces 620C
题目链接:https://codeforces.com/problemset/problem/620/C 题目分析 题意:给你一串珍珠,每个珍珠都有一个对应值,需要分割这n个珍珠(必须连续),使得每一 ...