五、同步

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. Sqlserver实现故障转移 — sqlserver镜像备份实现故障转移(3)

    目的:在已经加域的计算机上安装sqlserver2012,并配置数据库镜像实时同步,并实现故障转移. 在数据库层面实现故障自动转移后,应用程序里改怎么写数据库连接呢?其实使用ADO.NET或者SQL ...

  2. CDC类介绍

    CDC类 中文介绍 CDC类定义的是设备上下文对象的类. CDC对象提供处理显示器或打印机等设备上下文的成员函数,以及处理与窗口客户区对应的显示上下文的成员. 通过CDC对象的成员函数进行所有的绘图. ...

  3. Golang的一个CLI框架

    因为机缘巧合,因为希望能在VPS中使用百度网盘,了解到了一个开源的项目BaiduPCS-Go,可以用来直接存取访问百度网盘,做的相当不错 而且看ISSUES,作者可能还是个学生,很强的样子.稍微看了下 ...

  4. PHP 註解規則使用方式

    /** * 函數名稱 * 函數描述(有些會含HTML代碼) * * @access 變數可存取的權限 (Example: Public or Private) * @api 爲第三方來源的變數 * @ ...

  5. 抓包分析IP如何设置详细步骤

    首先,要知道的是,我们直接改以太网(校园网)的IP地址是不行的,校园网识别不了 如下图: 我们必须通过让电脑连接个人热点才能完成IP修改. 第一步,连接上热点后打开电脑的cmd命令程序,在命令窗口中输 ...

  6. MyBatis 简单入门

    添加maven 依赖 <dependencies> <dependency> <groupId>org.mybatis</groupId> <ar ...

  7. FTP简单搭建(一)

    一.FTP服务介绍 vsftp(very security ftp file transfer protocol 非常文件传输协议) FTP分为主动模式和被动模式. 主动模式:(不安全,传数据的端口是 ...

  8. Linux常用命令详解(1)

    基础命令: ls man pwd cd mkdir echo touch cp mv rm rmdir cat more less head tail clear poweroff reboot 命令 ...

  9. SolidWorks学习笔记1

    鼠标 修改缩放方向 点击工具->选项->视图,点击第一条,翻转鼠标滚轮缩放方向. 平移:Ctrl+ 中键 或者右键空白处 菜单选择平移 缩放:Shift+中键 或者右键空白处 菜单选择放大 ...

  10. 解决远程连不到CentOS7虚拟机或ifconfig中没有ens33

    在使用Secure CRT连接虚拟机连接不上,可能之前虚拟机关闭不当 登到虚拟机的中断使用ifconfig发现没有ens33 猜测是CentOS图形管理中的NetworkManager接管了网络配置, ...