MEC@JavaSE@实战篇@笔记03@线程安全与线程锁机制&&并发包【拓展、了解】

一、多行代码的线程安全问题【重点】

1、执行顺序的问题

火车卖票问题

public class AAA implements Runnable {
​
    //定义火车站一共有100张票
    int ticket = 100;
    @Override
    public void run() {
        //循环
        while(true){
            //判断有没有票
            if(ticket <= 0){
                break;
            }
​
            //如果有票就卖票
            System.out.println(Thread.currentThread().getName() +  "卖出了" + ticket + "号票");
            //给票减一
            ticket--;
        }
    }
}
​
public class Test01 {
    public static void main(String[] args) {
        AAA a = new AAA();
        //开启线程
        Thread t1 = new Thread(a);
        t1.setName("窗口一");
        t1.start();
​
        //开启线程
        Thread t2 = new Thread(a);
        t2.setName("窗口二");
        t2.start();
​
​
    }
}
​

图解

 

 

uploading.4e448015.gif转存失败重新上传取消

2、synchronized关键字

表示同步,同步的意思是一个线程在执行的时候别的线程只能等待。一个线程执行结束之后别的线程才能执行。

3、同步代码块

格式

synchronized(锁对象){

  同步代码

}

同步锁

锁对象可以是任意类型的对象

多个线程如果要同步必须使用同一个对象作为锁

代码演示

public class AAA implements Runnable {
​
    //定义火车站一共有100张票
    int ticket = 100;
​
    @Override
    public void run() {
        //循环
        while(true){
            //同步代码块
            synchronized ("abc") {
                //判断有没有票
                if (ticket <= 0) {
                    break;
                }
​
                //先获取当前线程对象,再获取线程名字
                String name = Thread.currentThread().getName();
                //如果有票就卖票
                System.out.println(name + "卖出了" + ticket + "号票");
​
                //给票减一
                ticket--;
            }
        }
    }
}
​
public class Test01 {
    public static void main(String[] args) {
        AAA a = new AAA();
        //开启线程
        Thread t1 = new Thread(a);
        t1.setName("窗口一");
        t1.start();
​
        //开启线程
        Thread t2 = new Thread(a);
        t2.setName("窗口二");
        t2.start();
    }
}

4、同步方法

格式

public synchronized void method(){
   同步代码
}

同步方法的锁对象

同步方法里面也是有锁对象的,但是锁对象不需要我们指定,同步方法的锁对象是固定的。

非静态同步方法:this (代表当前类的对象)

静态同步方法: 类的字节码对象(每一个类只有一个.class对象,所以这个对象一定是唯一的)

代码演示

public class AAA implements Runnable {
​
    //定义火车站一共有100张票
    int ticket = 100;
​
    @Override
    public void run() {
        //循环
        while(true){
            //判断有没有票
            if(ticket <= 0){
                break;
            }
            //调用方法
            method();
        }
    }
    //定义同步方法
    public  synchronized void method(){
        //再次判断
        if(ticket > 0) {
            //先获取当前线程对象,再获取线程名字
            String name = Thread.currentThread().getName();
            //如果有票就卖票
            System.out.println(name + "卖出了" + ticket + "号票");
​
            //给票减一
            ticket--;
        }
    } 
}
​
public class Test01 {
    public static void main(String[] args) {
​
        AAA a = new AAA();
        //开启线程
        Thread t1 = new Thread(a);
        t1.setName("窗口一");
        t1.start();
​
        //开启线程
        Thread t2 = new Thread(a);
        t2.setName("窗口二");
        t2.start();
​
    }
}

5、Lock锁

Lock锁的方式更符合面向对象的调用方式。更符合程序员的写代码习惯。

Lock是一个接口,有一个子类ReentrantLock

两个方法

public void lock()   :加同步锁
public void unlock() :释放同步锁

代码演示

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class AAA implements Runnable {
    //定义火车站一共有100张票
    int ticket = 100;
    //创建锁对象
    Lock lock = new ReentrantLock();
    
    public void run() {
        //循环
        while(true){
            //加锁
            lock.lock();
            //判断有没有票
            if(ticket <= 0){
                lock.unlock();
                break;
            }
            //先获取当前线程对象,再获取线程名字
            String name = Thread.currentThread().getName();
            //如果有票就卖票
            System.out.println(name +  "卖出了" + ticket + "号票");
            //给票减一
            ticket--;
            //解锁
            lock.unlock();
        }
    }
}
​
public class Test01 {
    public static void main(String[] args) {
​
        AAA a = new AAA();
        //开启线程
        Thread t1 = new Thread(a);
        t1.setName("窗口一");
        t1.start();
​
        //开启线程
        Thread t2 = new Thread(a);
        t2.setName("窗口二");
        t2.start();
​
    }
}

二、并发包

之前学习的集合类型都是会有线程安全问题的,如果遇到了多线程情况的,需要用并发包解决问题。

1、CopyOnWriteArrayList

ArrayList和CopyOnWriteArrayList效果演示

import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;

public class AAA extends Thread {
​
    //定义集合
    //static ArrayList<Integer> list = new ArrayList<>();
    //使用并发包集合
    static CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
​
    @Override
    public void run() {
        //给集合添加10000个元素
        for (int i = 0; i < 10000; i++) {
            list.add(i);
        }
    }
}
​
public class Test01 {
    public static void main(String[] args) throws InterruptedException {
​
        //开启线程
        new AAA().start();
        new AAA().start();
​
        //让主线程睡眠让循环先执行
        Thread.sleep(2000);
​
        //打印集合长度
        System.out.println(AAA.list.size());
​
        //问题一:可能长度小于20000
        //问题二:可能出现索引越界异常
​
    }
}

2、CopyOnWriteArraySet

HashSet和CopyOnWriteArraySet效果演示

import java.util.HashSet;
import java.util.concurrent.CopyOnWriteArraySet;
public class BBB extends Thread {
    //定义集合
    //static HashSet<Integer> set = new HashSet<>();
    //定义并发包集合
    static CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();
    @Override
    public void run() {
        //给集合添加10000个元素
        for (int i = 0; i < 10000; i++) {
            set.add(i);
        }
    }
}
​
public class Test02 {
    public static void main(String[] args) throws InterruptedException {
        //开启线程
        new BBB().start();
        new BBB().start();
​
        //让主线程睡眠让循环先执行
        Thread.sleep(2000);
        //打印集合长度
        System.out.println(BBB.set.size());
​
        //问题:集合的长度大于10000
​
    }
}
​

3、ConcurrentHashMap

3.1、HashMap和Hashtable和ConcurrentHashMap效果演示

import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;
​
public class CCC extends Thread {
​
    //创建集合
    //static HashMap<Integer,Integer> map = new HashMap<>();
​
    //使用Hashtable集合
    //static Hashtable<Integer,Integer> map = new Hashtable<>();
​
    //使用ConcurrentHashMap集合
    static ConcurrentHashMap<Integer,Integer> map = new ConcurrentHashMap<>();
​
    @Override
    public void run() {
        //给集合添加10000个元素
        for (int i = 0; i < 10000; i++) {
            map.put(i,i);
        }
    }
}
​
​
public class Test03 {
    public static void main(String[] args) throws InterruptedException {
        //开启线程
        new CCC().start();
        new CCC().start();
​
        //让主线程睡眠
        Thread.sleep(2000);
​
        //打印集合长度
        System.out.println(CCC.map.size());
​
        //问题:集合的长度大于10000
    }
}

2、Hashtable和ConcurrentHashMap的速度区别【重点】

Hashtable执行速度慢

ConcurrentHashMap执行速度快

import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;
public class CCC extends Thread {
    //创建集合
    //static HashMap<Integer,Integer> map = new HashMap<>();
    //使用Hashtable集合
//    static Hashtable<Integer,Integer> map = new Hashtable<>();
    //使用ConcurrentHashMap集合
    static ConcurrentHashMap<Integer,Integer> map = new ConcurrentHashMap<>();
​
    @Override
    public void run() {
        //获取系统当前时间
        long time1 = System.currentTimeMillis();
​
        //给集合添加10000个元素
        for (int i = 0; i < 10000; i++) {
            map.put(i,i);
        }
        //获取系统当前时间
        long time2 = System.currentTimeMillis();
​
        System.out.println( (time2-time1)  + "毫秒");
    }
}
​
​
public class Test04 {
    public static void main(String[] args) {
        //创建1000个线程
        for (int i = 0; i < 1000; i++) {
            new CCC().start();
        }
    }
}

3、速度区别的原因

Hashtable方法都是同步的,一个线程在执行的时候,别的线程只能等待。

public synchronized V put(K key, V value) {}

ConcurrentHashMap用到了CAS机制和部分同步代码块。

4、悲观锁和乐观锁

CAS机制称为乐观锁,执行效率高

同步机制称为悲观锁,执行效率低

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值