JUC学习(1)

创建线程的方式:
1.继承Thread类
Thread类中有一个start方法开启一个线程,在这个方法下面有一个start0的本地方法,意味着Java不能直接的创建线程。
start方法去调用重写的run方法
2.实现Runable接口
3.实现Callable接口
可以获取返回值。上面两种方法无法获取返回值。

public class UserCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> futureTask = new FutureTask<>(new Task());
        //开启线程
        new Thread(futureTask).start();
        //获取返回值
        Integer integer = futureTask.get();
        System.out.println(integer);
    }

    static class Task implements Callable<Integer>{
        public Integer call() throws Exception {
            return 1;
        }
    }
}

守护线程:
要将线程设置为守护线程,我们要做的就是调用Thread.setDeamon

Thread deamonThread = new Thread();
//开启守护线程
deamonThread.setDeamon(true);
deamonThread.start();

守护线程会随着被守护线程的结束而结束。

线程的状态:

public enum State {
        /**
         * 尚未启动的线程的线程状态。
         */
        NEW,

        /**
         * 可运行线程的线程状态。 处于可运行状态的线程正在
         *  Java 虚拟机中执行,但它可能正在等待来自操作系统
         * 的其他资源,例如处理器。
         */
        RUNNABLE,

        /**
         * 线程阻塞等待监视器锁的线程状态。 处于阻塞状态
         * 的线程正在等待监视器锁进入同步块/方法或在调用
         * Object.wait后重新进入同步块/方法。
         */
        BLOCKED,

        /**
         * 等待线程的线程状态。 由于调用以下方法之一,线程处于等待状态:
         * Object.wait没有超时
         * Thread.join没有超时
         * LockSupport.park
         * 处于等待状态的线程正在等待另一个线程执行特定操作。 例如,在对
         * 象上调用Object.wait()的线程正在等待另一个线程在该对象上
         * Object.notify()或Object.notifyAll() 。 调用Thread.join()
         *  的线程正在等待指定的线程终止。
         */
        WAITING,

        /**
         * 具有指定等待时间的等待线程的线程状态。 由于使用指定的正等待
         * 时间调用以下方法之一,线程处于定时等待状态:
         * Thread.sleep
         * Object.wait超时
         * Thread.join超时
         * LockSupport.parkNanos
         * LockSupport.parkUntil
         */
        TIMED_WAITING,

        /**
         * 终止线程的线程状态。 线程已完成执行。
         */
        TERMINATED;
    }

Synchronized锁谁:
方法
|–实例方法 —>锁实例对象
|–静态方法 —>锁类对象
代码块
|–this —>实例对象
|–类名.Class —>类对象

Lock锁

可以在创建锁的时候用构造方法控制。
公平锁:可以先来后到
非公平锁:可以插队(默认)

售票

出现问题的版本:出现票被重复卖等现象

public class SaleTickedDemo1 {
    public static void main(String[] args) {
        //公共资源
        Ticked ticked = new Ticked();
        new Thread(() -> { for (int i = 0; i < 60; i++) ticked.sale(); }, "A").start();
        new Thread(() -> { for (int i = 0; i < 60; i++) ticked.sale(); }, "B").start();
    }
}

class Ticked {
    //票数
    private int number = 50;
    //售票的方法
    public void sale() {
        //有票就卖
        if (number > 0) {
            try {
                //放大问题发生
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
        }
    }
}

使用Synchronized解决的版本:问题得到了解决

//方法上添加关键字synchronized
public synchronized void sale()

使用lock锁解决的版本:
这种写法不规范,可以使用try-finally上锁解锁

public  void sale() {
        //有票就卖
        if (number > 0) {
            try {
                //放大问题发生
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //加锁
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
            //解锁
            lock.unlock();
        }
    }

Synchronized和lock区别
1.Synchronized内置的Java关键字,lock是一个java类
2.Synchronized无法去判断获取锁的状态,lock可以判断
3.Synchronized会自动释放锁,lock必须手动放弃锁,如果不释放锁,会造成死锁
4.Synchronized线程1(获取锁,阻塞),线程(死等)lock可以尝试的获取锁,不会一直等待
5.Synchronized适合锁少量的代码块,lock适合锁大量的同步代码

消费者生产者问题

Synchronized版本:

public class ProviderConsumerDemo {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"生产者").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者").start();
    }
}
class Data{
    private int number = 0;
    //+1
    public synchronized void increment() throws InterruptedException {
        if (number!=0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,加完了
        this.notifyAll();
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        if (number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,减完了
        this.notifyAll();
    }
}

问题:上述的代码如果是一个生产者一个消费者的话没有问题,但是如果出现多个就会出现虚假唤醒的问题。
可以把Data中的increment和decrement方法中判断的if改为while,因为if只判断了一次。
Lock版本:
通过lock可以找到Condition,condition中await方法和signalAll跟wait和notifyAll效果一样

class Data{
    private int number = 0;
    Lock lock = new ReentrantLock();
    //获取condition
    Condition condition = lock.newCondition();

    //+1
    public  void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number!=0){
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程,加完了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    //-1
    public  void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (number==0){
                //等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程,减完了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

可以使用创建多个condition,使用await和signal配合,精准的控制线程唤醒。

集合类不安全

ArrayList
public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        new Thread(()->{
            list.add(UUID.randomUUID().toString().substring(0,5));
            System.out.println(Thread.currentThread().getName()+"=="+list);
        },String.valueOf(i)).start();
    }
}

出现问题:
ConcurrentModificationException 并发修改异常
解决:
1.使用Vector ,加了Synchronized关键字
2.Collections.synchronizedList(new ArrayList<>());
3.new CopyOnWriteArrayList<>();使用这个类
原理:
先复制一份,写入元素,然后放回去

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}
Set
public static void main(String[] args) {
    Set<String> set = new HashSet<>();
    for (int i = 0; i < 30; i++) {
        new Thread(()->{
            set.add(UUID.randomUUID().toString().substring(0,5));
            System.out.println(Thread.currentThread().getName()+"=="+set);
        },String.valueOf(i)).start();
    }
}

同样,还是会出现ConcurrentModificationException问题:
解决方法;
1.Collections.synchronizedSet(new HashSet<>());
2.new CopyOnWriteArraySet<>();

map
public static void main(String[] args) {
    Map<String,String> map = new HashMap<>();
     for (int i = 0; i < 30; i++) {
         new Thread(()->{
             map.put(UUID.randomUUID().toString().substring(0,5),"0");
             System.out.println(Thread.currentThread().getName()+"=="+map);
         },String.valueOf(i)).start();
     }
 }

不出所料的继续出现ConcurrentModificationException
解决:
1.new Hashtable<>(); 加了synchronized修饰方法
2.new ConcurrentHashMap()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值