多线程

1.同步方法和非同步方法是否可以同时调用

可以同时调用,可以理解为不加synchronized的方法无视这个对象的锁

    int count=10;
    public synchronized void m1(){
        count--;
        System.out.println(Thread.currentThread().getName()+"m1 started 01 : "+count);
        try {
            Thread.sleep(10000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"m1 started 02 : "+count);
    }
    public /*synchronized*/ void m2(){
        try {
            count--;
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"m2 start ..."+count);
    }
    public static void main(String[] args) {
        Test t = new Test();
        new Thread(()->t.m1(),"m1").start();
        new Thread(()->t.m2(),"m2").start();
    }

2.对业务写方法上枷锁,业务读方法上不加锁,可能会产生脏读现象(读到在写的过程中还没有完成的数据)

     String name;
     Double price;
    public synchronized void set(String name,Double price){
        this.name=name;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.price=price;
    }
    public Double get(){
        return this.price;
    }
    public static void main(String[] args) {
        Test t = new Test();
        new Thread(()->t.set("zhangsan",100.00),"m1").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(t.get());
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(t.get());
    }

3.一个同步方法可以调用另外一个同步方法,

一个线程已经拥有了某个对象的锁,再次申请的时候仍然会得到该对象的锁,也就是说synchronized获得的锁是可以重入的,子类也可以调用父类的同步方法

4.程序在执行过程中,如果出现异常

程序执行过程中如果出现异常,默认情况锁会被释放

所以在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况,若是不想释放这个锁则加上try()catch()

5.volatile

这个和JMM有关(Java Memory Model)Java内存模型

在JMM中有一个内存为主内存(我们所说的栈内存、堆内存都可以理解为主内存),每一个线程在执行的过程中都有一个自己的内存,存放自己变量的内存。它会从主内存中读取数据,把数据放进自己的缓冲区内,然后进行操作,然而在没操作完之前,它不会再回到主内存中取读取数据,因为本地已经缓存了数据,操作完之后再写回主内存中。若是读的这条数据加上volatile,这条数据发生变化之后,就会通知其他线程,之前的数据过期了,请重新来主内存中读取数据。

注:volatile不能替代synchronized,volatile只能保证可见性,synchronized技能保证可见性,还能保证原子性,synchronized性能比volatile低

 volatile boolean running=true;
    void m(){
        System.out.println("m start");
        while (running){
        }
        System.out.println("m end");
    }

    public static void main(String[] args) {
        Test3 t = new Test3();
        new Thread(()->t.m(),"m1").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t.running=false;
    }

6.AtomicInteger

AtomicXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的。

7.细粒度的锁效率要比粗粒度的锁效率高

8.synchronized所对象

所是锁的是对象,则对的是堆内存中的对象,若是新new了一个对象,则这个对象已经不是原来的对象了,所以不能新new 这个对象。

9.不要以字符串常量作为锁的对象 

10.wait、sleep、notify

wait释放锁,sleep不释放锁,notify也不释放锁。wait和notify都必须在synchronized同步代码块中使用,同样也是争夺的对象上的wait和notify

public class Test4 {
    List list = new ArrayList();
    public void add(Object obj){
        list.add(obj);
    }
    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        Test4 t = new Test4();
        final Object obj = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj){
                    System.out.println("线程2启动");
                    if(t.size()!=5){
                        try {
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程2结束");
                    obj.notify();
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj){
                    System.out.println("线程1启动");
                    for (int i=0;i<10;i++){
                        System.out.println("add :"+i);
                        t.add(obj);
                        if (t.size()==5){
                            obj.notify();
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }).start();
    }
}

 11.CountDownLatch

使用Latch(门闩)代替wait notify 来进行通知,好处是通信方式简单,同时也可以指定等待时间,使用await和countdown方法代替wait和notify,CountDownLatch不涉及锁定,当count的值为零时(每一次执行countdown方法时都会减一),当前线程继续执行。当不涉及同步,只是涉及线程通信的时候,用CountDownLatch。

public class Test4 {
    List list = new ArrayList();
    public void add(Object obj){
        list.add(obj);
    }
    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        Test4 t = new Test4();
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(new Runnable() {
            @Override
            public void run() {
                    System.out.println("线程2启动");
                    if(t.size()!=5){
                        try {
                            latch.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程2结束");
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                    System.out.println("线程1启动");
                    for (int i=0;i<10;i++){
                        System.out.println("add :"+i);
                        t.add(latch);

                        if (t.size()==5){
                            latch.countDown();
                        }
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                }
            }
        }).start();
    }
}

 12.ReenTrantLock

使用reentrantlock同样可以锁住对象,需要注意的是reentrantlock必须要必须要必须要手动释放锁

使用syn锁定的话若果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,因此经常在finally中进行锁的释放

注:这个用起来比较灵活,他有一个方法为:lock.trylock(); 返回的是布尔类型的值,拿着锁会怎样执行,没有拿着锁另一种方式执行,这样可以进行判断,还可以给他指定一个特定的时间,lock.trylock(5,TimeUtil.SECCONDS),指定5秒后。ck.lockInterruptibly()也可以打断自己等锁,去执行其他的任务,打断方法为t2.interrupt();Lock lock = new ReentrantLock(true)这个是公平锁

public class Test6 {
    Lock lock = new ReentrantLock();
    public void m1(){
        lock.lock();
        System.out.println("线程 m1 启动");
        try {
            for (int i=0;i<5;i++){
                System.out.println("do : "+i);
                TimeUnit.SECONDS.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void m2(){
        lock.lock();
        System.out.println("线程 m2 启动");
        lock.unlock();
    }

    public static void main(String[] args) {
        Test6 t = new Test6();
        new Thread(t::m1).start();
        new Thread(t::m2).start();
    }

/*public void m2(){
Boolean locked=false;
try {
locked = lock.tryLock(2,TimeUnit.SECONDS);
if (locked){
System.out.println("线程 m2 拿到锁 并 启动");
}else {
System.out.println("线程 m2没有拿到锁 启动");
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if (locked)lock.unlock();
}
}*/
}


打断线程的等待

public class Test6 {
    Lock lock = new ReentrantLock(true);
    public void m1(){
        lock.lock();
        System.out.println("线程 m1 启动");
        try {
            for (int i=0;i<5;i++){
                System.out.println("do : "+i);
                SECONDS.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void m2(){
        try {
//            lock.lock();
            System.out.println("线程2 等待获取锁");
            //若是长时间拿不到锁,自己停止等待,继续执行其他程序
            lock.lockInterruptibly();
//            lock.tryLock(2, SECONDS);
//            SECONDS.sleep(2);
            System.out.println("正常执行");
        } catch (InterruptedException e) {
            System.out.println("线程2不再等待锁,去执行其他任务");
        }finally {
            try{
                lock.unlock();
            }catch (Exception e){
                System.out.println("没有获取锁");
            }
        }

    }

    public static void main(String[] args) {
        Test6 t = new Test6();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                t.m1();
            }
        });
        thread1.start();
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                t.m2();
            }
        });
        thread2.start();
        try {
            SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.interrupt();
    }
}
原文地址:https://www.cnblogs.com/gxlaqj/p/11691190.html