B014Java学习笔记-线程同步机制

一、线程安全问题概述

B014Java学习笔记-线程同步机制_第1张图片

 

二、线程安全问题的代码实现

多线程类:

package study.thread;

public class ThreadSafeImpl implements Runnable {
    private int ticket = 100;
    @Override
    public void run() {
        while (true){
            if(ticket>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
                ticket--;
            }else {
                break;
            }
        }
    }
}

测试类:

package study.thread;

public class ThreadSafeTest {
    public static void main(String[] args) {
        ThreadSafeImpl threadSafe = new ThreadSafeImpl();
        //多线程干一件事
        new Thread(threadSafe).start();
        new Thread(threadSafe).start();
        new Thread(threadSafe).start();
    }
}

运行结果(截取部分):

(发现票卖重了,而且卖了不存在的票,这就有问题了!)
售票员Thread-2正在卖第100张票……
售票员Thread-0正在卖第100张票……
售票员Thread-1正在卖第100张票……
...
售票员Thread-2正在卖第1张票……
售票员Thread-0正在卖第1张票……
售票员Thread-1正在卖第-1张票……

 

三、线程安全问题产生的原理

 

四、解决线程安全问题

1、引入线程同步机制的三种方法

①同步代码块;

②同步方法;

③锁机制;

2、同步代码块

格式:

synchronized(锁对象){
    可能出现线程安全问题的代码(从访问到共享数据的代码开始)
}

锁对象的含义:

前面的线程开始执行后回去拿取堆内存中的锁对象,后面的线程开始执行后再去拿锁对象就拿不到了,所以无法继续执行,需要等待前面的线程执行完毕,归还锁对象,后面的线程才能拿到锁对象,继续执行;

注意:

①通过代码块中的锁对象,可以使用任意的对象;

②但必须保证多个线程使用的所对象是同一个;

③所对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行;

代码示例:

加入同步代码块后的多线程类:

package study.thread;

public class ThreadSafeImpl implements Runnable {
    private int ticket = 100;
    private final Object object = new Object();
    @Override
    public void run() {
        while (true){
            synchronized (object){
                if(ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}

测试类:

package study.thread;

public class ThreadSafeTest {
    public static void main(String[] args) {
        ThreadSafeImpl threadSafe = new ThreadSafeImpl();
        //多线程干一件事
        new Thread(threadSafe).start();
        new Thread(threadSafe).start();
        new Thread(threadSafe).start();
    }
}

运行结果(截取部分):

售票员Thread-0正在卖第100张票……
售票员Thread-0正在卖第99张票……
售票员Thread-2正在卖第98张票……
售票员Thread-1正在卖第97张票……
售票员Thread-1正在卖第96张票……
售票员Thread-1正在卖第95张票……

3、同步代码块的原理

4、同步方法

使用步骤:

①把访问了共享数据的代码抽取出来,放到一个方法中;

②在方法上添加synchronized修饰符;

格式

访问修饰符 synchronized 返回值类型 方法名(参数列表){
    //方法体
}

代码示例:

package study.thread;

public class ThreadSafeImpl implements Runnable {
    private int ticket = 100;
    @Override
    public void run() {
        printMessage();
    }
    private synchronized void printMessage(){
        while (true){
            if(ticket>0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
                ticket--;
            }else {
                break;
            }
        }
    }
}

但经测试发现,此方法一个会出现一个线程将票卖完的情况,我自己猜测也许是因为while循环放进了锁住的方法中。

改进的代码:

package study.thread;

public class ThreadSafeImpl implements Runnable {
    private int ticket = 100;
    @Override
    public void run() {
        while (true){
            printMessage();
            if(ticket==0){
                break;
            }
        }
    }
    private synchronized void printMessage(){
        if(ticket>0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
            ticket--;
        }
    }
}

这样写确实避免了上述情况。

备注:

同步方法也会把方法内部的代码锁住,只让一个线程执行,实际上所的对象是new RunnableImpl(),也就是this(自身)

5、静态同步方法

概述:

静态同步方法就是在一般的同步方法synchronized前加上static;

注意:

此时的所对象不是this(本身),而是本类的class属性-->class文件对象(反射)

6、锁机制(Lock锁)

概述:

Lock接口实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作;

使用步骤:

①在成员位置创建一个;

②在有可能出现安全问题的代码前,调用获取锁的方法(闭锁);

③在有可能出现安全问题的代码后,调用释放锁的方法(开锁);

代码示例:

package study.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadSafeImpl implements Runnable {
    private int ticket = 100;
    //1、在成员位置创建一个
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            //2、在有可能出现安全问题的代码前,调用获取锁的方法(闭锁)
            lock.lock();
            if(ticket>0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
                ticket--;
            }else {
                break;
            }
            //3、在有可能出现安全问题的代码后,调用释放锁的方法(开锁)
            lock.unlock();
        }
    }
}

代码示例更好的写法:

package study.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadSafeImpl implements Runnable {
    private int ticket = 100;
    //1、在成员位置创建一个
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            //2、在有可能出现安全问题的代码前,调用获取锁的方法(闭锁)
            lock.lock();
            if(ticket>0){
                try {
                    Thread.sleep(10);
                    System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //3、在有可能出现安全问题的代码后,调用释放锁的方法(开锁)
                    lock.unlock();
                }
            }else {
                break;
            }
        }
    }
}

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Java,多线程,线程同步机制)