并发编程学习(一)

基本概念:

synchronized上加static和不加的区别就是有static是类锁,不加就是对象锁。

线程安全:当多个线程访问某一个类(对象或方法)时,这个类始终能表现出正确的行为,那么这个类就是一个线程安全的。

synchronized:可以在任何对象及方法上加锁,而加锁的这段代码称为"互斥区"或"临界区"。

线程安全:

public class MyThread extends Thread{
    private int count = 5;

    public  void run(){
        count--;
        System.out.println(this.currentThread().getName()+" count = "+count);
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread,"t1");
        Thread t2 = new Thread(myThread,"t2");
        Thread t3 = new Thread(myThread,"t3");
        Thread t4 = new Thread(myThread,"t4");
        Thread t5 = new Thread(myThread,"t5");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}
输出结果:
t1 count = 4
t4 count = 2
t3 count = 1
t2 count = 3
t5 count = 0

可以看出并没有按照我们程序的顺序调用,要实现线程安全那就要加synchronized;

   public  synchronized void run(){
        count--;
        System.out.println(this.currentThread().getName()+" count = "+count);
    }

 有时候我们会调用同一对象内的两个方法,一个加锁,一个未加锁,未加锁的不会受加锁的影响。

以下程序会出现脏读:

public class MyObject {
    private String name = "test";
    private String pwd = "123";

    public synchronized void setValue(String name,String pwd){
        this.name = name;

        try{
            Thread.sleep(2000);//睡眠2秒,但name值已经设置过了
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        this.pwd = pwd;

        System.out.println("set结果name="+name+",pwd="+pwd);
    }

    public void getValue(){
        System.out.println("get结果name="+name+",pwd="+pwd);
    }

    public static void main(String[] args) throws InterruptedException {
        MyObject myObject = new MyObject();//同一对象
        Thread t1 = new Thread(new Runnable() {//主线程
            @Override
            public void run() {
                myObject.setValue("wt","456");
            }
        });

        t1.start();//调set方法
        Thread.sleep(1000);//休眠一秒调get方法

        myObject.getValue();
    }
}
执行结果:
get结果name=wt,pwd=123
set结果name=wt,pwd=456
原因:调set方法时休眠了两秒,但name值已经设置过了,get方法没加锁,所以取出来是只设置name的值。
public synchronized void getValue(){//get方法加锁输出结果就不会出现脏读
System.out.println("get结果name="+name+",pwd="+pwd);
}


Volatile关键字:

   作用:就是强制线程到主内存里去读取变量,而不去线程工作内存区里去读取,从而实现了多个线程间的变量可见,也就是满足线程安全的可见性。

  线程可以执行的操作有use(使用)、assign赋值、load装载、store存储、锁定、解锁。

  主内存的操作有:read,write,lock,unlock每个操作都是原子性的。

 volatile一般用于只针对多个线程可见的变量操作,并不能代替synchronzied的同步功能,它不具有原子性,要实现原子 性建议使用atomic类的系统对象,

public class RunThread extends Thread {
    private boolean isRunning = true;
    private void setRunning(boolean isRunning){
        this.isRunning = isRunning;
    }

    public void run(){
        System.out.println("进入run方法");
        while (isRunning == true){

        }
        System.out.println("线程停止");
    }

    public static void main(String[] args) throws InterruptedException {
        RunThread rt = new RunThread();
        rt.start();

        Thread.sleep(3000);

        rt.setRunning(false);
        System.out.println("isrunning设置为false");
        Thread.sleep(1000);

        System.out.println("isRunning:"+rt.isRunning);
    }
}
输出结果:
进入run方法
isrunning设置为false
isRunning:false


程序仍然处于运行中
加入volatile后:private volatile boolean isRunning = true;
执行结果:
进入run方法
isrunning设置为false
线程停止
isRunning:false

出现上面的结果是因为,每个线程都有自己的一个内存区域(这样运行更快),在程序运行时,把需要的数据装载到自己的工作内存,而主内存中的数据仍然存在,我们上面程序修改isRunning为false,只是修改了主内存中的数据,线程的工作内存并没有修改,所以会出现主程序走完了,线程还在执行。

当isRunning被volatile修饰时,变量改变时会强制线程执行引擎去主内存中读取。

猜你喜欢

转载自www.cnblogs.com/javage/p/9496546.html
今日推荐