手记

java基础thread——java5之后的多线程(浅尝辄止)

承上启下

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。

一、JDK5中Lock锁的使用

void lock()  上锁

void unlock()  释放锁

代码示意:

public class SellTicket implements Runnable {    private int ticket = 20;    private Lock lock = new ReentrantLock();    @Override
    public void run() {        while (true){
                lock.lock();                if (ticket <= 0) {                    break;
                }                //卖票这个动作不安全。
                System.out.println(Thread.currentThread().getName() + "正在售卖第" + (ticket--) + "票");
                lock.unlock();
                System.out.println(Thread.currentThread().getName()+"结束");
            }

    }
}

首先我们要造一个锁

Lock lock = new ReentrantLock();

然后调用lock.lock()和lock.unlock()将需要上锁的代码包起来。

但是查看java的一些源码,还是synchronized用的多。

虽然线程有了锁解决了安全问题,但是偶尔也会因为失误操作出现死锁的情况。

同步弊端:

  • 效率低

  • 如果出现了同步嵌套,就容易产生死锁问题

什么是死锁:

是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象。

示例:

首先造两个锁:

public class MyLock {    // 创建两把锁对象
    public static final Object objA = new Object();    public static final Object objB = new Object();
}

同步代码块嵌套:

public class DieLock extends Thread {    private boolean flag;    public DieLock(boolean flag) {        this.flag = flag;
    }    @Override
    public void run() {        if (flag) {            synchronized (MyLock.objA) {
                System.out.println("if objA");                synchronized (MyLock.objB) {
                    System.out.println("if objB");
                }
            }
        } else {            synchronized (MyLock.objB) {
                System.out.println("else objB");                synchronized (MyLock.objA) {
                    System.out.println("else objA");
                }
            }
        }
    }
}

测试:

public class DieLockDemo {
    public static void main(String[] args) {
        DieLock dl1 = new DieLock(true);
        DieLock dl2 = new DieLock(false);

        dl1.start();
        dl2.start();
    }
}

运行打印:if objAelse objB

二、线程间通信

生产者、消费者模式:

生产者没有就生产,有就等待消费者消费;消费者有就消费,没有就等待生产者生产。

java提供了等待唤醒的机制。

Object类中提供了三个方法:

wait():等待

notify():唤醒单个线程

notifyAll():唤醒所有线程

代码示例:

public class Student {
    String name;    int age;    boolean flag;
}

生产者:

public class SetThread implements Runnable {    private Student s;    public SetThread(Student s){        this.s = s;
    }    private int x = 0;    @Override
    public void run() {        while (true){            synchronized (s) {                if (s.flag){                    try {
                        s.wait();//t1等着,释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }                if (x % 2 == 0) {
                    s.age=20;
                    s.name="徐繁韵";
                } else {
                    s.age=21;
                    s.name="唐富平";
                }
                x++;
                s.flag=true;
                s.notify();//唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。
            }            //t1有,或者t2有
        }
    }
}

消费者:

public class GetThread implements Runnable {    private Student s;    public GetThread(Student s){        this.s = s;
    }    @Override
    public void run() {        while (true){            synchronized (s) {                if (!s.flag){                    try {
                        s.wait();//t2等待,立即释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(s.name + ":" + s.age);
                s.flag=false;
                s.notify();//唤醒t1,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。
            }
        }

    }
}

测试:

public class StudentDemo {
    public static void main(String[] args) {
        Student s = new Student();
        SetThread st = new SetThread(s);
        GetThread gt = new GetThread(s);
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(gt);
        t1.start();
        t2.start();
    }
}

输出打印:
徐繁韵:20唐富平:21徐繁韵:20唐富平:21徐繁韵:20唐富平:21徐繁韵:20唐富平:21徐繁韵:20。
。
。

看的出是生产一条消费一条。为了实现线程间的通信,将共同操作的数据通过有参构造器传入线程。

思考一个问题,为什么等待唤醒的方法不定义在Thread里呢?

这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。所以,这些方法必须定义在Object类中。

栗子优化:

既然wait()、notify()、notifyAll()定义在锁对象里,那么我们把前面的栗子优化一下。

把Student的成员变量给私有化,把设置和获取的操作给封装成功能,并加上同步。设置或者获取的线程里面只需要调用方法即可。

public class Student {    private String name;    private int age;    boolean flag;    public synchronized  void  set(String name,int age){        if (flag){            try {                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }        this.name = name;        this.age = age;        this.flag = true;        this.notify();
    }    public synchronized  void get(){        if (!this.flag){            try {                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(this.name+":"+this.age);        this.flag = false;        this.notify();
    }
}public class SetThread implements Runnable {    private Student s;    public SetThread(Student s){        this.s = s;
    }    private int x = 0;    @Override
    public void run() {        while (true){                if (x % 2 == 0) {
                   s.set("徐繁韵",20);
                } else {
                   s.set("唐富平",21);
                }
                x++;
            }
        }
}public class GetThread implements Runnable {    private Student s;    public GetThread(Student s){        this.s = s;
    }    @Override
    public void run() {        while (true){
            s.get();
        }
    }
}public class StudentDemo {    public static void main(String[] args) {
        Student s = new Student();
        SetThread st = new SetThread(s);
        GetThread gt = new GetThread(s);
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(gt);
        t1.start();
        t2.start();
    }
}

线程的状态转换图:



作者:韵呀
链接:https://www.jianshu.com/p/38da355903a5


0人推荐
随时随地看视频
慕课网APP