五、同步

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的更多相关文章

  1. Java 多线程(Thread) 同步(synchronized) 以及 wait, notify 相关 [实例介绍]

    场景描述 有一家很大的商场,在某市有几个商品配送中心,并有几家分店,这家商场经营很多的商品,平时运营情况是这样的: 根据各分店的商品销售情况,给分店配送相应需求量的商品:并上架到分店指让的位置,供客户 ...

  2. java多线程(三)——锁机制synchronized(同步语句块)

    用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解 ...

  3. java——多线程的实现方式、三种办法解决线程赛跑、多线程数据同步(synchronized)、死锁

    多线程的实现方式:demo1.demo2 demo1:继承Thread类,重写run()方法 package thread_test; public class ThreadDemo1 extends ...

  4. Java多线程:线程同步与关键字synchronized

    一.同步的特性1. 不必同步类中所有的方法, 类可以同时拥有同步和非同步方法.2. 如果线程拥有同步和非同步方法, 则非同步方法可以被多个线程自由访问而不受锁的限制. 参见实验1:http://blo ...

  5. Java多线程-同步:synchronized 和线程通信:生产者消费者模式

    大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...

  6. Java多线程同步 synchronized 关键字的使用

    代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D)运行完这个方法后再运行此线程A, ...

  7. Java多线程(三)—— synchronized关键字详解

    一.多线程的同步 1.为什么要引入同步机制 在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源.必须对这种潜在资源冲突进行预防. 解决方法:在线程使用一个资源时为其加锁即可. 访问资 ...

  8. “全栈2019”Java多线程第十六章:同步synchronized关键字详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  9. Java自学-多线程 同步synchronized

    Java 多线程同步 synchronized 多线程的同步问题指的是多个线程同时修改一个数据的时候,可能导致的问题 多线程的问题,又叫Concurrency 问题 步骤 1 : 演示同步问题 假设盖 ...

随机推荐

  1. CentOS6.5升级手动安装GCC4.8.2 转载

    一.简易安装 操作环境 CentOS6.5 64bit,原版本4.4.7,不能支持C++11的特性~,希望升级到4.8.2 不能通过yum的方法升级,需要自己手动下载安装包并编译 1.1 获取安装包并 ...

  2. Django-ORM外键属性总结

    ForeignKey ForeignKey(ForeignObject) # ForeignObject(RelatedField) to, # 要进行关联的表名 to_field=None, # 要 ...

  3. 121. 买卖股票的最佳时机( Best Time to Buy and Sell Stock)

    题目地址:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/ 解题思路一:暴力求解法 根据题目我们可以知道,我们知道最大 ...

  4. meta viewport移动端自适应

    参考链接:https://www.jianshu.com/p/561357d7cd7b

  5. 用grok拆分java日志

    1.假设一行日志内容如下: [root@VM_0_92_centos opt]# cat error.log -- ::,[ERROR ajp-nio--exec-](cn.com.al1.compo ...

  6. python控制流 -- if、for、while、range()、sys.exit()

    1.布尔值 “布尔”数据类型只有两种:True和False    #首字母以T或F开头,后面小写,且不能作为变量赋值 2.比较操作符 == 等于 != 不等于 <  小于 >  大于 &l ...

  7. python中迭代器和生成器的详细解释

    https://www.cnblogs.com/wilber2013/p/4652531.html

  8. 最好用linux版QQ

    这个版本的qq是见过linux下做好用的qq,希望对大家有用; 安装简单,qq易用,不卡死,可以接收文件. 安装过程如下: git clone https://gitee.com/wszqkzqk/d ...

  9. Java Web开发技术教程入门-自定义标签

    回顾: 昨天了解了JSP开发的两种模式Model1和Model2模式.Model1采用JSP+JavaBean技术开发Web应用,它比较适合小规模应用的开发,效率较高,易于实现.但由于在Model1中 ...

  10. WPF中Matrix介绍

    最近在做一些图形变换操作的功能,图形变换涉及大学中的矩阵运算部分的知识,又重新复习了一下矩阵.这里做一下记录.由于不知道矩阵如何输入,一个个截图又麻烦,所以这里就全部用截图了^-^.