版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/havebeenstand/article/details/83445937
线程安全问题(demo:银行转账):多个线程操作相同一个数据的时候就会出现线程安全问题。
程序举例:
public class SynchronizedStudy {
class Output{
public void printName(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
private void init(){
final Output o = new Output();
//启动两个线程
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
o.printName("wangjinglong");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
o.printName("123456");
}
}
}).start();
}
public static void main(String[] args) {
new SynchronizedStudy().init();
}
}
运行结果:
出现线程安全问题
使用synchronize(使用同一个对象才能达到互斥的效果)
修改以上代码
class Output{
public void printName(String name){
synchronized (name) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
这样仍然存在线程问题,因为name不是同一个对象。
再次对原来的代码修改
class Output{
public void printName(String name){
String s = "xx";
synchronized (s) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
使得对同一个对象进行互斥,线程安全问题不再出现。
再次对原来的代码修改
private void init(){
final Output o = new Output();
//启动两个线程
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//修改以前o.printName("wangjinglong");
//修改以后
new Output().printName("wangjinglong");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//修改以前o.printName("123456");
//修改以后
new Output().printName("123456");
}
}
}).start();
}
又会产生线程安全问题,因为不是同一个对象。
再次对原来的代码修改
class Output{
public void printName(String name){
synchronized (this) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
线程安全问题解除,因为this是指当前调用它的对象,而两个线程中是用的Output的同一个对象,所以实现了互斥访问。
等价于这样写
class Output{
public synchronized void printName(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
一个方法中对同一个对象的限制只能用一个synchronize,否则会发送死锁,如以下所示:
class Output{
public synchronized void printName(String name){
synchronized (this) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
这样依然能实现互斥(两个线程一个调用printName,另一个调用printName1):
class Output{
public synchronized void printName(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
public synchronized void printName1(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
再次修改(两个线程一个调用printName,另一个调用printName3):
static class Output{
public synchronized void printName(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
public synchronized void printName1(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
public static synchronized void printName3(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
这样修改,又会出现线程安全问题。
解决问题(如下所示):
static class Output{
public void printName(String name){
synchronized (Output.class) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
public synchronized void printName1(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
public static void printName3(String name){
synchronized (Output.class) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}