`

Java 多线程例子6 线程安全 线程同步 同步代码块 同步函数

阅读更多

线程安全

出现线程安全就是在使用多线程的时候程序出现了不期望的结果。

怎样思考线程安全:线程中任何一步运行完都可能交出CPU的控制权。

下面是一个可能出现线程安全的例子:

class ThreadDemo {
	public static void main(String[] args) {
		TestThread t = new TestThread();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
	}
}

class TestThread implements Runnable {
	int tickets = 100;
	public void run() {
		while(true) {
			if(tickets<=0)
				break;
			//try{Thread.sleep(10);}catch(Exception e) {}
			System.out.println(Thread.currentThread().getName() 
				+ " is saling " + tickets--);
		}
	}
}

 例子展示了四个售票员一起卖这100张票,那么卖出同样序号的票是我们不期望看到了情况吧,上面已经提到了,分析线程安全的最基本的方法就是认为线程运行的时候任何情况下都可以交出CPU的控制权,上面一共启动了4个线程,现在假设tickets的值为1时,线程1在运行完“if(tickets<=0)”后交出CPU,后线程2得到了CPU,卖出了票1,后线程1重新得到了CPU,又卖出了票1或0。当然,上述情况是我们假设在什么情况下会出现象这样的线程安全的问题,在一般情况,我们运行这个程序好几次也不能看出这样的线程安全问题,那我们怎么确定这个问题呢?我们可以用“sleep(100)”来主动交出CPU来验证我们的想法:去掉上面注释的sleep再运行程序,得到

运行结果 写道
Thread-1 is saling 2
Thread-2 is saling 1
Thread-3 is saling 0
Thread-0 is saling -1
Thread-1 is saling -2

 

卖出了0、-1和-2的票,证明上述这种写法是存在线程安全的。

线程同步

为了解决线程安全的问题,就要引入线程同步。

class ThreadDemo {
	public static void main(String[] args) {
		TestThread t = new TestThread();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
	}
}

class TestThread implements Runnable {
	int tickets = 100;
	String str = "";
	public void run() {
		while(true) {
			synchronized(str) {
				if(tickets<=0)
					break;
				//try{Thread.sleep(10);}catch(Exception e) {}
				System.out.println(Thread.currentThread().getName() 
					+ " is saling " + tickets--);
			}
		}
	}
}

 在上面的代码中加入synchronized,就可以实现线程同步了。

运行结果 写道
hread-2 is saling 5
hread-3 is saling 4
hread-1 is saling 3
hread-0 is saling 2
hread-2 is saling 1

 

这个线程同步怎么理解呢?这里有个锁旗标的概念,锁旗标可以理解为java中的每一个对象都有个标志位,该标志位开始的状态是1,当执行完synchronized后这个对象的标志位被置为了0,这个过程就说这个线程得到了这个对象的锁旗标,synchronized块运行完之后这个线程会让出这个对象的锁旗标,而每个线程在遇到synchronized是都回查看这个对象的锁旗标在不在,如果不在该线程就会主要让出CPU。

这里还要记住synchronized的对象一定要是多个线程的公共对象,要是各自的对象就不能实现同步了。如下面改变str定义的位置。

class ThreadDemo {
	public static void main(String[] args) {
		TestThread t = new TestThread();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
	}
}

class TestThread implements Runnable {
	int tickets = 100;
	public void run() {
		String str = "";
		while(true) {
			synchronized(str) {
				if(tickets<=0)
					break;
				//try{Thread.sleep(10);}catch(Exception e) {}
				System.out.println(Thread.currentThread().getName() 
					+ " is saling " + tickets--);
			}
		}
	}
}

 

结果为 写道
Thread-1 is saling 2
Thread-2 is saling 1
Thread-3 is saling 0
Thread-0 is saling -1
Thread-1 is saling -2

 另外,在运行加入了synchronized同步块的程序的时会发现速度明显比没有同步块的程序要慢的多,所以在确定不会出现线程安全问题的程序中不要加入同步块,就像我们经常先使用Vector还是有ArrayList呢?它们两个的功能基本是完全一样的都是List,而Vector中的函数考虑了线程同步,ArrayList没有,这下就非常明显了,如果需要保证线程安全是就用Vector,不需要就用ArrayList效率高些。

同步函数

同步函数实现

class ThreadDemo {
	public static void main(String[] args) {
		TestThread t = new TestThread();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
	}
}

class TestThread implements Runnable {
	int tickets = 100;
	public void run() {
		while(true) {
			sale();
		}
	}
	public synchronized void sale() {
		if(tickets<=0)
				return;
		try{Thread.sleep(10);}catch(Exception e) {}
		System.out.println(Thread.currentThread().getName() 
					+ " is saling " + tickets--);
	}
}

 同步函数实际上同步的是this对象,这样如果要想某一个同步块和一个同步函数同步,就在同步块中使用this对象。

分享到:
评论
1 楼 wpf523 2014-07-14  
mark

相关推荐

    Java多线程机制(讲述java里面与多线程有关的函数)

    Java多线程机制 9.1 Java中的线程 9.2 Thread的子类创建线程 9.3 使用Runable接口 9.4 线程的常用方法 9.5 GUI线程 9.6 线程同步 9.7 在同步方法中使用wait()、notify 和notifyAll()方法 9.8 挂起、恢复和终止线程 ...

    Java多线程实现回调函数.txt

    Java多线程实现 自定义 的 回调函数. 手写 完成,亲测 完成,可以 直接运行,Demo ,具有一定参考价值,供大家阅读 下载,谢谢

    Java多线程技术实现矩阵乘积计算

    适合初学者使用,Java多线程技术实现矩阵乘积计算

    Java多线程矩阵相乘的代码

    一个用Java写的多线程相乘代码,及其测试。

    关于C#多线程的函数示例

    用c#实现的 多线程小例子 不带参数/带参数的多线程函数示例

    C语言使用多线程例子

    C语言使用多线程例子,使用createthread这个windows--api函数来生成多线程。

    JAVA多线程编程详解-详细操作例子(转自CSDN)

    Runnable 接口只有一个方法 run(),我们声明自己的类实现 Runnable 接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 Thread ...

    Qt 多线程及简单实例 demo

    3.多线程对代码的敏感度高,因此对代码的细微修改都可能产生意想不到的效果。 先由一个简单的例子引出多线程 先作出这个简单的界面 “开始”对应的槽函数是:slotStart() “停止”对应的槽函数是:slotStop() ...

    图解java多线程

    本文主要讲java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的线程函数用法、概述等。首先让我们来了解下在操作系统中进程和线程的区别:

    一个JavaScript多线程函数

    一个JavaScript多线程函数 一个JavaScript多线程函数 一个JavaScript多线程函数 一个JavaScript多线程函数

    Java多线程开发(构造函数的使用)

    本文主要总结了Java多线程开发中线程创建时多用到的创建方式(不同的构造函数)以及线程组相关的基础知识

    C++JNI多线程回调java

    C++使用JNI多线程回调java代码例子,因为依赖了我的一些库和头文件,没有传上去,编译不过,但是大家可以参考一下,如何使用C++多线程调用java函数

    当析构函数遇到多线程── C++ 中线程安全的对象回调

    本文源自我在 2009 年 12 月上海 C++ 技术大会的一场演讲《当析构函数遇到多线程》,内容略有增删。原始 PPT 可从 http://download.csdn.net/source/1982430 下载。 本文读者应具有 C++ 多线程编程经验,熟悉互斥器...

    《当析构函数遇到多线程》PPT

    这是陈硕在 2009 年上海 C++ 技术大会演讲《当析构函数遇到多线程》的 PPT 投影片,可自由用于个人学习,其他使用需得到作者许可。 简介:编写线程安全的类不是难事,用同步原语保护内部状态即可。但是对象的生与死...

    Android JNI多线程编程回调JAVA函数

    通过JNI接口静态注册的native方法去创建线程,同时提供native回调Java的方法。通过这个框架可以去实现线程监听某一个状态,然后回调Java的方法(如发消息去通知顶层,实现显示)

    多线程API函数

    多线程API函数详解,很不错的一本书哦,值得下载

    多线程笔记多线程笔记

    使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义、实例化和启动新线程。 一个Thread类实例只是一个对象,像Java中的任何其他对象一样,具有变量和方法,生死于堆上。 Java中,每个线程都有一个...

    JAVA多线程运用

    (2)、创建TestCounter类,在该类中定义main函数,在main函数中定义Storage对象、Counter对象和 Printer对象,创建Counter线程和Printer线程并启动 (3)、保存文件,调试并编译运行程序。 参考程序运行效果: 3. ...

    解析Java的多线程机制

    而Java应用程序中的多线程则是共享同一应用系统资源的多个并行代码执行 体,线程之间的通信和协调方法相对简单。 可以说:Java语言对应用程序多线程能力的支持增强了Java作为网络程序设计语言的优势,为实现分布式...

    Java操作Oracle数据库-多线程.rar

    Java操作Oracle数据库-多线程,演示了使用多线程技术从oracle 数据库中读取函数、存储过程等功能。,具体实现方法请详见源代码。

Global site tag (gtag.js) - Google Analytics