线程安全问题主要出现在访问临界资源的时候,就是访问同一个对象的时候,可能会出现无法挽回的损失,特别是在关于资金安全方面的时候,当然还有数据库事务方面的问题。他们很类似,都是要保证数据的原子性。
那么在java中如何保证线程安全呢?
对与共同使用的对象进行加锁,意思是我使用的时候,那么你就必须等待,等我用完之后你再用,反之依然。就像上厕所,你去的时候我是不能去的。
如何加锁呢?下面写三个加锁的方式
首先看一下实例代码
- public class TraditionalSynchornizedTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- new TraditonalSynchornizedTest().sartThread();
- }
- public void sartThread(){
- final Outerput outerput = new Outerput();
- new Thread(new Runnable(){
- @Override
- public void run() {
- while(true){
- try {
- Thread.sleep(5);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- outerput.print("zhangsanfeng");
- }
- }
- }).start();
- new Thread(new Runnable(){
- @Override
- public void run() {
- while(true){
- try {
- Thread.sleep(5);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- outerput.print("luxiaofeng");
- }
- }
- }).start();
- }
- public class Outerput{
- public void print(String name){
- for(int i = 0;i < name.length(); i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- }
以上代码没有对共同持有的对象outerput加锁,所以会出现线程安全问题
1、对代码块加锁
对共同持有的对象加锁可以把内部类写成这样的
- public class Outerput{
- public void print(String name){
- synchronized (this) {
- for(int i = 0;i < name.length(); i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- }
2、对非静态方法加锁,加锁的对象是this
- public class Outerput{
- public synchronized void print(String name){
- for(int i = 0;i < name.length(); i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
3、对静态方法加锁的对象到底是谁?
- public static synchronized void print2(String name){
- for(int i = 0;i < name.length(); i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136/*多线程_等待唤醒机制*/
/**
*需求:创建两个线程,一个进行存储信息,一个进行打印信息。
*目的:程序输入一个"mike man"打印一个"mike man"
* 输入一个"莉莉 女女女女女"打印一个"莉莉 女女女女女"
*步骤:创建一个共享资源类RescourceWaitNotifyDemo
* 创建输入线程类InputWaitNotifyDemo
* 创建打印线程类OutputWaitNotifyDemo
* 由主函数创建线程,并执行。
*问题:
* 为什么会有线程安全问题?
* 怎么解决?
*/
/*线程共享资源类,信息类*/
class ResourceWaitNotifyDemo //
{
private String name;
private String sex;
private boolean flag=false;
public synchronized void setInfo(String name,String sex)//信息输入函数
{
if(flag)
/*如果flag值为false,则共享资源中没有信息,进行赋值
如果为true,则共享资源中有信息没有打印,不进行赋值,进行wait操作
*/
{
try
{
this.wait();
}
catch (Exception e)
{
}
}
else
{
this.name = name;
this.sex = sex;
}
flag=true; //改变开关,让输入程序不能再次进行赋值,同时可以让打印程序启动
this.notify();//唤醒输出线程
}
public synchronized void OutInfo()//信息输出函数
{
if (!flag)//如果flag值为ture,则共享资源中有信息,进行打印,否则等待
{
try
{
this.wait();
}
catch (Exception e)
{
}
}
else
{
System.out.println(name+"......"+sex);
}
flag=false; //改变开关,让输入程序可以进行赋值,同时让自己不能在打印
this.notify();//唤醒输入线程
}
}
/*输入线程*/
class InputWaitNotifyDemo implements Runnable
{
private ResourceWaitNotifyDemo r; //创建共享类引用
InputWaitNotifyDemo(ResourceWaitNotifyDemo r)
{
this.r = r; //接收共享类对象
}
public void run()
{
int x=0;
while(true)
{
if (x==0)
{
r.setInfo("mike","man");
}
else
{
r.setInfo("莉莉","女女女女女");
}
x=(x+1)%2;
}
}
}
/*输出线程*/
class OutputWaitNotifyDemo implements Runnable
{
private ResourceWaitNotifyDemo r;
OutputWaitNotifyDemo(ResourceWaitNotifyDemo r)
{
this.r = r; //和输入线程操作同一个共享类对象
}
public void run()
{
while (true)
{
r.OutInfo();
}
}
}
/*主函数*/
class ThreadWaitNotifyTest
{
public static void main(String[] args)
{
ResourceWaitNotifyDemo res = new ResourceWaitNotifyDemo();
/*
InputWaitNotifyDemo i = new InputWaitNotifyDemo(res);
OutputWaitNotifyDemo o = new OutputWaitNotifyDemo(res);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
*/
new
Thread(
new
InputWaitNotifyDemo(res)).start();
//创建输入线程
new
Thread(
new
OutputWaitNotifyDemo(res)).start();
//创建打印线程
}
}