1.什么是线程同步?
多线程编程是很有趣的事情,它很容易出现"错误情况",这种情况不是由编码造成的,它是由系统的线程调度造成的,当使用多个线程来访问同一个数据时,很容易出现"偶然情况",出现线程安全问题.
线程安全问题最常见的就是银行取钱问题,铁路售票问题,必须保证甲方在操作数据时候,己方不会影响甲方.类似于公共厕所,一个人占一个坑.
2.下面的例子,将说明为什么要保证线程安全?
package com.amos.concurrent;
/**
* @ClassName: ThreadSynchronizedTest
* @Description: 多线程并发之线程同步
* @author: amosli
* @email:hi_amos@outlook.com
* @date Apr 20, 2014 2:44:29 PM
*/
public class ThreadSynchronizedTest {
public static void main(String[] args) {
new ThreadSynchronizedTest().init();
}
private void init() {
final OutPuter outPuter = new OutPuter();
//新建一个线程A
new Thread(new Runnable() {
public void run() {
while (true) {
//休息10ms
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
outPuter.output("hi_amos");//输出
}
}
}).start();
//线程B
new Thread(new Runnable() {
public void run() {
while (true) {
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
outPuter.output("amosli");
}
}
}).start();
}
class OutPuter {
//输出name,逐个字节读取,并输出
public void output(String name) {
int length = name.length();
synchronized (name) {
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
}
输出结果如下图所示:
在多次执行上面的代码后就会发现出现问题了,这是因为线程A和线程B在执行output方法时,系统调度出现了问题,导致了上面的问题,这种情况出现的概率相对较小,但这种小概率的事件也是要解决的.下面将介绍如何解决这种问题.
3.解决方法1--同步代码块
只需要将上面的方法加上synchronized关键字即可
public void output2(String name) {
int length = name.length();
synchronized (this) {
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
在要多次访问的代码块前加上synchronized关键字,即表示加上排队系统,线程A只有等线程B执行完了才能访问同一个代码块.
这里要注意this,this表示的是当前对象,这里this也可以用Outputer.class代替.
4.解决方法2--同步方法
public synchronized void output(String name) { int length = name.length(); for (int i = 0; i < length; i++) { System.out.print(name.charAt(i)); } System.out.println(); }
同步方法,即是在要多次访问的方法前面加上synchronized关键字.
注意:
1.synchronized关键字可以修饰方法,代码块,但不能修饰构造器,属性等;
2.同时synchronized关键字最好一个方法中只用一次,否则可能造成死锁.
3.任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定.
上面的代码可以改写如下:
package com.amos.concurrent;
/**
* @ClassName: ThreadSynchronizedTest
* @Description: 多线程并发之线程安全
* @author: amosli
* @email:hi_amos@outlook.com
* @date Apr 20, 2014 2:44:29 PM
*/
public class ThreadSynchronizedTest {
public static void main(String[] args) {
new ThreadSynchronizedTest().init();
}
private void init() {
final OutPuter outPuter = new OutPuter();
//新建一个线程
new Thread(new Runnable() {
public void run() {
while (true) {
//休息10ms
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
outPuter.output("hi_amos");//输出
}
}
}).start();
new Thread(new Runnable() {
public void run() {
while (true) {
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
outPuter.output("amosli");
}
}
}).start();
}
static class OutPuter {
//输出name,逐个字节读取,并输出
public synchronized void output(String name) {
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
public void output2(String name) {
int length = name.length();
synchronized (OutPuter.class) {
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
public synchronized static void output3(String name) {
int length = name.length();
for (int i = 0; i < length; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}