代码理解多线程(一)

 

多线程访问会有什么问题??

 

public class TestMyThread extends Thread{
    

    //共享变量
    private int a = 5;
    
    //重写run方法
    @Override
    public  void  run(){        
        a--;
        System.out.println("a is :"+ a);    
    }
    
    public static void main(String[] args) {
        
        
        //实列需要多线程的对象
        TestMyThread myThread = new TestMyThread();
        
        
        //创建多个线程实列
        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();
        
        
    }
    
    
    
    
    
}

结果:

a is :3
a is :3
a is :2
a is :1
a is :0

a is :4
a is :3
a is :2
a is :1
a is :0

等等,你会发现每次出来的结果都不一样,这就是多线程访问的问题

 怎样解决???

package test19218;

import test517designpattern.test;

/**
 * @Description
 * @Author zengzhiqiang
 * @Date 2019年2月18日
 */

public class TestMyThread extends Thread{
    

    //共享变量
    private int a = 5;
    
    //重写run方法
    @Override
    public synchronized void  run(){        
        a--;
        System.out.println("a is :"+ a);    
    }
    
    public static void main(String[] args) {
        
        
        //实列需要多线程的对象
        TestMyThread myThread = new TestMyThread();
        
        //创建多个线程实列
        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();
        
        
    }
    
    
    
    
    
}

 

 我们加了synchronize关键字

a is :4
a is :3
a is :2
a is :1
a is :0

 结果是我们想要的。

那么synchronize是锁的对象myThread ,还是那一段代码呢?

package test19218;

import test517designpattern.test;

/**
 * @Description
 * @Author zengzhiqiang
 * @Date 2019年2月18日
 */

public class TestMyThread extends Thread{
    
    //共享变量
    private int a = 5;
    
    //重写run方法
    @Override
    public synchronized void  run(){        
        a--;
        System.out.println("a is :"+ a);    
    }
    
    public static void main(String[] args) {
        
        
        //实列需要多线程的对象
        TestMyThread myThread = new TestMyThread();
        TestMyThread myThread2 = new TestMyThread();
        
        //创建多个线程实列
        Thread t1 = new Thread(myThread,"t1");
        Thread t2 = new Thread(myThread2,"t2");
        
        //运行
        t1.start();
        t2.start();
        
    }
    
}

 

 结果

a is :4
a is :4

 说明锁住的是代码。如果是锁住的对象,那么就应该减两次才对!!!

 /**
 * 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁,
 * 所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁(Lock),
 *
 * 在静态方法上加synchronized关键字,表示锁定.class类,类一级别的锁(独占.class类)。
 * @author alienware
 *
 */

 如果同一个对象,两个方法,一个用synchronize修饰,另一个不用,我访问非synchronize修饰的方法是否需要等待,其实就是是否会阻塞?

package test19218;

/**
 * @Description
 * @Author zengzhiqiang
 * @Date 2019年2月18日
 */

public class TestMyThread02 {

    String tag;

    public TestMyThread02(String tag) {
        this.tag = tag;
    }

    public TestMyThread02() {
    }

    // 共享变量
    private int a = 5;

    // 方法
    public synchronized void deNum(String tag) throws InterruptedException {
        a--;
        Thread.sleep(1000);
        System.out.println(tag + "---a is :" + a);
    }

    // 方法
    public  void adNum(String tag) {
        a++;
        System.out.println(tag + "---a is :" + a);
    }

    public static void main(String[] args) {

        final String t1 = "t1";
        final String t2 = "t2";

        // 实列需要多线程的对象
        final TestMyThread02 myThread01 = new TestMyThread02();
        // final TestMyThread02 myThread02 = new TestMyThread02();

        // 创建多个线程实列
        Thread th = new Thread(new Runnable() {

            @Override
            public void run() {

                try {
                    myThread01.deNum(t1);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        });

        Thread th1 = new Thread(new Runnable() {

            @Override
            public void run() {

                myThread01.adNum(t2);

            }
        });

        // 运行
        th.start();
        th1.start();
    }

}

 

 结果

t2---a is :5
t1---a is :5

 t2先执行了,所以并不会阻塞。

如果两个方法都加上synchronize呢?

package test19218;

/**
 * @Description
 * @Author zengzhiqiang
 * @Date 2019年2月18日
 */

public class TestMyThread02 {

    String tag;

    public TestMyThread02(String tag) {
        this.tag = tag;
    }

    public TestMyThread02() {
    }

    // 共享变量
    private int a = 5;

    // 方法
    public synchronized void deNum(String tag) throws InterruptedException {
        a--;
        Thread.sleep(1000);
        System.out.println(tag + "---a is :" + a);
    }

    // 方法
    public synchronized  void adNum(String tag) {
        a++;
        System.out.println(tag + "---a is :" + a);
    }

    public static void main(String[] args) {

        final String t1 = "t1";
        final String t2 = "t2";

        // 实列需要多线程的对象
        final TestMyThread02 myThread01 = new TestMyThread02();
        // final TestMyThread02 myThread02 = new TestMyThread02();

        // 创建多个线程实列
        Thread th = new Thread(new Runnable() {

            @Override
            public void run() {

                try {
                    myThread01.deNum(t1);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        });

        Thread th1 = new Thread(new Runnable() {

            @Override
            public void run() {

                myThread01.adNum(t2);

            }
        });

        // 运行
        th.start();
        th1.start();
    }

}

 

 t1---a is :4
t2---a is :5

 进行阻塞

所以,当同一个对象中的两个方法有逻辑联系的时候,你其中一个加了synchronize,那么你另一个也应该加上,才能保证返回结果的正确。

 

锁对象改变问题

package test19218;

/**
 * @Description
 * @Author zengzhiqiang
 * @Date 2019年2月18日
 */

public class TestMyThread02 {

    private String tag = "tag";

    public TestMyThread02() {
    }

    // 共享变量
    private int a = 5;

    // 方法
    public void deNum() throws InterruptedException {

        synchronized (tag) {
            tag = "change tag";

            System.out
                    .println(Thread.currentThread().getName() + "sleep begin");
            Thread.sleep(6000);
            System.out.println(Thread.currentThread().getName() + "sleep over");
        }

    }

    // 方法
    public synchronized void adNum(String tag) {
        a++;
        System.out.println(tag + "---a is :" + a);
    }

    public static void main(String[] args) {

        final String t1 = "t1";
        final String t2 = "t2";

        // 实列需要多线程的对象
        final TestMyThread02 myThread01 = new TestMyThread02();
        // final TestMyThread02 myThread02 = new TestMyThread02();

        // 创建多个线程实列
        Thread th = new Thread(new Runnable() {

            @Override
            public void run() {

                try {
                    myThread01.deNum();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }, t1);

        Thread th1 = new Thread(new Runnable() {

            @Override
            public void run() {

                try {
                    myThread01.deNum();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }, t2);

        // 运行
        th.start();

        try {
            th1.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        th1.start();
    }

}

 

 th进入锁以后改变对象,th1立刻就进入,没有等t1执行完成

如果不改变锁对象,那么th1将等th执行完成后才进入。

 死锁问题,

在设计程序时就应该避免双方相互持有对方的锁的情况

package com.bjsxt.base.sync006;

/**
 * 死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况
 * @author alienware
 *
 */
public class DeadLock implements Runnable{

    private String tag;
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();
    
    public void setTag(String tag){
        this.tag = tag;
    }
    
    @Override
    public void run() {
        if(tag.equals("a")){
            synchronized (lock1) {
                try {
                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock1执行");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock2执行");
                }
            }
        }
        if(tag.equals("b")){
            synchronized (lock2) {
                try {
                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock2执行");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock1执行");
                }
            }
        }
    }
    
    public static void main(String[] args) {
        
        DeadLock d1 = new DeadLock();
        d1.setTag("a");
        DeadLock d2 = new DeadLock();
        d2.setTag("b");
        
        Thread t1 = new Thread(d1, "t1");
        Thread t2 = new Thread(d2, "t2");
        
        t1.start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
    

    
}

 

同一对象属性的修改不会影响锁的情况

 

package com.bjsxt.base.sync006;
/**
 * 同一对象属性的修改不会影响锁的情况
 * @author alienware
 *
 */
public class ModifyLock {
    
    private String name ;
    private int age ;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    public synchronized void changeAttributte(String name, int age) {
        try {
            System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 开始");
            this.setName(name);
            this.setAge(age);
            
            System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 修改对象内容为: "
                    + this.getName() + ", " + this.getAge());
            
            Thread.sleep(2000);
            System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        final ModifyLock modifyLock = new ModifyLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                modifyLock.changeAttributte("张三", 20);
            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                modifyLock.changeAttributte("李四", 21);
            }
        },"t2");
        
        t1.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
    
}

 

 使用synchronized代码块加锁,比较灵活

 

package com.bjsxt.base.sync006;

/**
 * 使用synchronized代码块加锁,比较灵活
 * @author alienware
 *
 */
public class ObjectLock {

    public void method1(){
        synchronized (this) {    //对象锁
            try {
                System.out.println("do method1..");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public void method2(){        //类锁
        synchronized (ObjectLock.class) {
            try {
                System.out.println("do method2..");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    private Object lock = new Object();
    public void method3(){        //任何对象锁
        synchronized (lock) {
            try {
                System.out.println("do method3..");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    public static void main(String[] args) {
        
        final ObjectLock objLock = new ObjectLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                objLock.method1();
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                objLock.method2();
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                objLock.method3();
            }
        });
        
        t1.start();
        t2.start();
        t3.start();
        
        
    }
    
}

 

 synchronized代码块对字符串的锁,注意String常量池的缓存功能

package com.bjsxt.base.sync006;
/**
 * synchronized代码块对字符串的锁,注意String常量池的缓存功能
 * @author alienware
 *
 */
public class StringLock {

    public void method() {
        //new String("字符串常量")
        synchronized ("字符串常量") {
            try {
                while(true){
                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + "开始");
                    Thread.sleep(1000);        
                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + "结束");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        final StringLock stringLock = new StringLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                stringLock.method();
            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                stringLock.method();
            }
        },"t2");
        
        t1.start();
        t2.start();
    }
}

 

 使用synchronized代码块减小锁的粒度,提高性能

package com.bjsxt.base.sync006;

/**
 * 使用synchronized代码块减小锁的粒度,提高性能
 * @author alienware
 *
 */
public class Optimize {

    public void doLongTimeTask(){
        try {
            
            System.out.println("当前线程开始:" + Thread.currentThread().getName() +
                    ", 正在执行一个较长时间的业务操作,其内容不需要同步");
            Thread.sleep(2000);
            
            synchronized(this){
                System.out.println("当前线程:" + Thread.currentThread().getName() +
                    ", 执行同步代码块,对其同步变量进行操作");
                Thread.sleep(1000);
            }
            System.out.println("当前线程结束:" + Thread.currentThread().getName() +
                    ", 执行完毕");
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        final Optimize otz = new Optimize();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                otz.doLongTimeTask();
            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                otz.doLongTimeTask();
            }
        },"t2");
        t1.start();
        t2.start();
        
    }
    
    
}

 

 多个addAndGet在一个方法内是非原子性的,需要加synchronized进行修饰,保证4个addAndGet整体原子性

package other;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description
 * @Author zengzhiqiang
 * @Date 2019年2月19日
 */

public class AtomicTest {

    //变量为atomic系列
    private  AtomicInteger num = new AtomicInteger(0);
    
    //多次自增 synchronized
    public   void addNum(){
     int i =    num.incrementAndGet();
     System.out.println("now  num is :" + i);
    
     num.incrementAndGet();
     num.incrementAndGet();
     num.incrementAndGet();
     num.incrementAndGet();
    
    }
    
    
    public static void main(String[] args) {
        
        
        //实列需要多线程对象
        final AtomicTest test  = new AtomicTest();
        
        
        //起多个线程运行
        for(int i = 0;i<=100;i++){
            
            //System.out.println("now time is : "+ i);
            Thread t = new Thread(new Runnable() {                
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    test.addNum();
                }
            });
            
            //启动
            t.start();
        }
    }
    
}

 

结果:

now  num is :418
now  num is :433
now  num is :432

 结果是乱的,加上synchronize之后

    //多次自增 synchronized
    public  synchronized   void addNum(){
     int i =    num.incrementAndGet();
     System.out.println("now  num is :" + i);
    
     num.incrementAndGet();
     num.incrementAndGet();
     num.incrementAndGet();
     num.incrementAndGet();
    
    }

 now  num is :486
now  num is :491
now  num is :496
now  num is :501

 输出结果总是一直且有序

 volatile关键字不具备synchronized关键字的原子性(同步)

 

package other;

import java.util.ArrayList;
import java.util.List;

import com.mysql.fabric.xmlrpc.base.Array;

/**
 * @Description
 * @Author zengzhiqiang
 * @Date 2019年2月19日
 */

public class VolatileTest {

    //变量
    private volatile int num  = 0;
    
    
    //变量的自增   synchronized
    public  void addNum(){
        num++;
        num++;
        System.out.println("now num is :" + num);
    }
    
    public static void main(String[] args) {
        
        //多线程实列对象
        final VolatileTest test =  new VolatileTest();
        
        //定义多线程列表
        List<Thread> list = new  ArrayList<Thread>();
        
        
        //初始化多个线程
        for(int i = 0;i<100;i++){
            Thread t = new Thread(new Runnable() {
                
                @Override
                public void run() {
                test.addNum();
                    
                }
            });
            
            list.add(t);
        }
        
        
        //启动多线程
        for(Thread th:list){
            th.start();
        }
        
    }
    
}

 

 结果

now num is :188
now num is :188
now num is :186
now num is :186

 无序,显示不正确,但是结果为200,正确

使用synchronize修饰

    //变量的自增   synchronized
    public  synchronized  void addNum(){
        num++;
        num++;
        System.out.println("now num is :" + num);
    }

结果

now num is :194
now num is :196
now num is :198
now num is :200

 有序,显示正确,且结果正确。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值