Java线程安全

目录

线程不安全原因:

 解决方法

解决方案

1.同步代码块

2.同步方法

3.同步静态方法

死锁

线程间通讯

1.休眠

wait与sleep的区别:

 2.唤醒

3.生产者,消费者模式整一个


线程不安全原因:

多个线程对同一个数据进行操作。

多个线程对同一个数据进行操作可能会因为争夺CPU使用次数不同,导致执行顺序不同,从而产生结果与预期不一致

public static void main(String[] args) {
		
		MyPiao myPiao = new MyPiao();
		MyPiao myPiao2 = new MyPiao();
		MyPiao myPiao3 = new MyPiao();
		
		Thread thread = new Thread(myPiao,"窗口1");
		Thread thread2 = new Thread(myPiao2,"窗口2");
		Thread thread3 = new Thread(myPiao3,"窗口3");
		
		thread.start();
		thread2.start();
		thread3.start();
		
	}

}

class MyPiao implements Runnable{
	
	private static  int num=1000;

	public MyPiao() {
		super();
		
		// TODO Auto-generated constructor stub
	}
	
	public void run() {
		// TODO Auto-generated method stub
		while (num>0) {
			
			
			Thread thread = Thread.currentThread();
			String nameString =thread.getName();
			num--;
			System.out.println(nameString+"卖了第"+num+"张");
			
		}
		
	}

例如:此代码线程进入while循环中时,num--,getName,打印,有可能这次线程争到了执行权,而下次没执行到,又有一个线程一直争取到了,导致打印结果会出现重复的

 解决方法

                使多个线程只对一个数据进行操作

解决方案

1.同步代码块

语法:        synchronized (锁对象) {

                                 要同步的代码         

                   }

上锁:当一个线程进入synchronizend代码块中,其锁对象就会上锁,此时别的线程无法进入,只能在外边等待

开锁:当一个线程执行完synchronizend代码块中的代码,此锁对象就会开锁

注意:所有对象都可以作为锁对象

           要保证多个线程的锁对象是同一个对象

private static final Object Lock=new Object();

Object是所有对象的父类,可以选用它创建的对象作为锁

有了以上工具可以对买票进行操作

先创建Object类的对象

private static final Object Lock=new Object();

 再用其上锁

		while (num>0) {
	
				synchronized (Lock) {
					
				
					if(num<=0) {//防止最后执行的两个线程不知道num<0了
						return;//num用static修饰,是唯一的,当有窗口执行到0张时,
					}
					Thread thread=Thread.currentThread();
					String nameString =thread.getName();
					num--;//防止再进入到此处执行num--
					System.out.println(nameString+"卖了第"+num+"张");
					
					
				}
			
			}

此时不会再重复

2.同步方法

语法:访问权限修饰符 synchronized 返回值类型 方法名(形参列表){

                                方法体(也就是需要同步的代码块)

                }

注意:同步方法的锁对象,是调用该方法的对象(同步方法锁对象即是this)

	public void run() {
		
	sell();
	}
	public synchronized void sell() {//同步方法
		
		while (num>0) {
			
			

				if(num<=0) {//防止最后的两个线程不知道num<0了
					return;
				}
				Thread thread=Thread.currentThread();
				String nameString =thread.getName();
				num--;
				System.out.println(nameString+"卖了第"+num+"张");
								
			
		
		}

此方法也可完成任务

3.同步静态方法

语法:

        访问权限修饰符 synchronizat static 返回值类型 方法名(形参列表){

                        方法体(要同步的代码)

        }

同步静态方法的锁对象是该方法所属的类的对象

类对象:

                一个类被JVM加载时会产生一个类对象,该对象包含该类的所有信息,如该类的包名,类名,父类名,实现 的接口名,属性数量,属性名,方法数量,方法名等信息 因为一个类只会被加载一次,所以一个类只有一个类对象

死锁

死锁产生的原因是多个线程互相持有对方所需的锁资源

为避免尽量不要在同步中使用同步

public class Test {
	public static void main(String[] args) {
		final Object  Lock1 = new Object();
		final Object  Lock2 = new Object();		
		Thread thread1 = new Thread(new Runnable() {			
			@Override
			public void run() {			
			synchronized (Lock1) {
				System.out.println("thread1的锁");			
				synchronized (Lock2) {
					System.out.println("thread2的锁");					
				}				
			}			
			}
		});
		
		Thread thread2 = new Thread(new Runnable() {			
			@Override
			public void run() {
			synchronized (Lock2) {
				System.out.println("thread2的锁");			
				synchronized (Lock1) {
					System.out.println("thread1的锁");				
				}				
			}				
			}
		});
		
		thread1.start();
		thread2.start();	
}
}

 可以看到线程1,线程2,互相要对方的锁,产生了死锁。对于这个例子来说,只需改锁的顺序就可解决

Thread thread1 = new Thread(new Runnable() {			
			@Override
			public void run() {			
			synchronized (Lock1) {
				System.out.println("thread1的锁");			
				synchronized (Lock2) {
					System.out.println("thread2的锁");					
				}				
			}			
			}
		});
		
		Thread thread2 = new Thread(new Runnable() {			
			@Override
			public void run() {
			synchronized (Lock1) {
				System.out.println("thread2的锁");			
				synchronized (Lock2) {
					System.out.println("thread1的锁");				
				}				
			}				
			}
		});

改变锁的顺序

线程间通讯

        1.方法由Object类提供

        2.只能在同步代码块中使用,原因: 防止唤醒在睡眠之前,让通讯顺序与自己所想一样

        3.必须使用该同步代码块的锁对象或同步方法的锁对象调用

1.休眠

        锁对象.wait();

                作用:让当前线程陷入无限期休眠

        锁对象.wait(timeout);

                作用:让当前线程有限期休眠,参数为休眠时间,单位:毫秒  ms

        锁对象.wait(timeout, nanos);

                作用:让当前线程有限期休眠,休眠时间毫秒+纳秒

                                参数1休眠时间,单位毫秒ms

                                 参数2休眠时间,单位纳秒ns

wait与sleep的区别:

                wait:

                        由Object类提供的普通方法

                        只能在同步中使用

                        休眠期间会释放持有锁对象

                sleep:

                        Threak类提供的静态方法

                        可在同步中使用,也可在同步外使用

                        休眠期间不会释放所有的锁对象

        sleep方法是当前线程休眠,让出cpu,不释放锁,这是Thread的静态方法;wait方法是当前线程等待,释放锁,这是Object的方法。

 2.唤醒

        锁对象.notify();

                作用:随机唤醒一个使用 调用该方法的对象 作为 锁对象 的线程

        锁对象.notifyAll();

                作用:唤醒所有 使用该方法的对象 作为 锁对象 的线程

练习:龟兔赛跑,龟跑100米,1m/s,兔80米睡觉,10m/s,龟到终点叫兔子


public class Test1 {
	
	public static final Object cokek= new Object();	
	public static void main(String[] args) {		
		
		Thread Tu = new Thread(new Runnable() {			
			@Override
			public void run() {				
				for (int i = 0; i <101; i=i+10) {
					System.out.println("兔跑了"+i+"米");
					if(i==80) {
						System.out.println("睡了");
						synchronized (cokek) {//锁内使用
							try {
								cokek.wait();//休眠
							} catch (InterruptedException e) {								
								e.printStackTrace();
							}
						}
					}
				}
				System.out.println("兔完了");				
			}
		});
				
		Thread Gui = new Thread(new Runnable() {			
			@Override
			public void run() {						
				for (int i = 0; i <101; i++) {
					System.out.println("龟跑了"+i+"米");
				}
				System.out.println("乌龟已完");
				synchronized (cokek) {//锁内使用
					cokek.notify();//龟唤醒所有进程
				}				
			}
		});
		Gui.start();
		Tu.start();
			
	}
}

 练习:

1.使用两个线程 1个线程打印1~52 1个线程打印a~z

                要求:打印输出结果为12a34b56c78d910e1112f....5152z

     原理:是1进程停止,让2进程打开,2运行,2停止,让1打开,1停止,如此循环。

所以如果某次抢夺CPU失败,有可能运行失败,等会 2题 试试新方法

package work7;

public class Name implements Runnable {
	@Override
	public void run() {
		for (int i = 97; i < 123; i++) {		
			try {
				Thread.sleep(10);//保证数字先打,字母睡一会
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			System.out.print((char)i);
			synchronized (Colek.LOCK) {			
				Colek.LOCK.notify();//唤醒对方,因为只有一个,所以随机必定是对方				
				try {
					Colek.LOCK.wait();//自己睡了,等对面唤醒
				} catch (InterruptedException e) {
					e.printStackTrace();
				}	
			}			
		}	
		
	}
	
}
package work7;

public class Zeioz implements Runnable {
	@Override
	public void run() {		
		for (int i = 1; i < 52; i=i+2) {			
			System.out.print(i);//打印数字
			System.out.print(i+1);//打印这个数字后面的数字
			synchronized (Colek.LOCK) {				
						Colek.LOCK.notify();//唤醒对方,因为只有一个,所以随机必定是对方				
					try {
						Colek.LOCK.wait();//自己睡了,等对面唤醒
					} catch (InterruptedException e) {
					e.printStackTrace();
					}				
			} 

		}
		
	}

}
package work7;

public class Test {
	
	public static void main(String[] args) {
		Zeioz zeioz = new Zeioz();
		
		Name name = new Name();
		
		Thread a = new Thread(zeioz);
		Thread b = new Thread(name);
		
		a.start();
		b.start();
	}

}

3.生产者,消费者模式整一个

package work9;
public class Gongc {//工厂
	public static int nam=0;//设定初始商品
	public static final int Mix=1001;//仓库上限1001个	
	public synchronized void Shengc() {//生产方法
			Thread thread=Thread.currentThread();//获取线程名字有用
		if (nam<Mix) {//商品小于仓库就生产
			nam++;//生产
			System.out.println(thread.getName()+"生产的,还有"+nam+"个");		
				this.notifyAll();//主要让销售启动				
		}else {//大于的话满了		
			System.out.println("仓库已满"+thread.getName()+"停止生产");
			try {
				this.wait();//停下生产
			} catch (InterruptedException e) {			
				e.printStackTrace();
			}
		}		
	}
	
	public synchronized void Chus() {//销售方法
		Thread thread=Thread.currentThread();
		if(nam>0) {//大于0有货
			nam--;//销售	
			System.out.println(thread.getName()+"销售的,还有"+nam+"个");			
			this.notifyAll();//主要生产启动下
		}
		else {	
			System.out.println(thread.getName()+"停止销售,已经销售完");
			try {
				this.wait();//没了,停止销售
			} catch (InterruptedException e) {			
				e.printStackTrace();
			}
		}		
	}
}
package work9;
public class Xiaos implements Runnable {//销售
	private Gongc gongc;//	
	public Xiaos() {
		super();		
	}
	public Xiaos( Gongc gongc) {
		super();	
		this.gongc = gongc;
	}
	@Override
	public void run() {		
		while (true) {//007别停下来						
		gongc.Chus();//出售方法
		}		
	}
}
package work9;
public class Shengc implements Runnable{//生产
	private Gongc gongc;	
	public Shengc() {
		super();		
	}
	public Shengc(Gongc gongc) {
		super();
		this.gongc = gongc;
	}
	@Override
	public void run() {		
		while (true) {//007别停		
			gongc.Shengc();//生产方法
		}
	}
}
package work9;
public interface Suo {	
	public static final Object Lock=new Object();//Object整个锁
}

package work9;
public class Test {
	public static void main(String[] args) {
		Gongc gongc = new Gongc();
		Xiaos xiaos = new Xiaos(gongc);
		Shengc shengc = new Shengc(gongc);
		
		Thread thread = new Thread(shengc,"小王");
		Thread thread2 = new Thread(shengc,"小贺");
		Thread thread3 = new Thread(shengc,"小高");
		Thread thread4 = new Thread(shengc,"老八");
		
		
		Thread thread5 = new Thread(xiaos,"小莉");
		Thread thread6 = new Thread(xiaos,"翠翠");
		Thread thread7 = new Thread(xiaos,"小花");
		
		thread.start();
		thread2.start();
		thread3.start();
		thread4.start();
		
		thread5.start();
		thread6.start();
		thread7.start();
		
	}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值