【15】线程基本使用入门

(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)好好活就是做有意义的事情.
(8)亡羊补牢,为时未晚
(9)科技领域,没有捷径与投机取巧。
(10)有实力,一年365天都是应聘的旺季,没实力,天天都是应聘的淡季。
(11)基础不牢,地动天摇
(12)写博客初心:成长自己,辅助他人。当某一天离开人世,希望博客中的思想还能帮人指引方向.
(13)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

线程基本使用入门

1.多线程与Android线程性能优化2

1.1类锁对象锁显示锁

1.1.1什么是锁?

(1)锁,即大门,很多陌生人,就进不去,很安全。
(2)如果没有锁,无法锁这个大门,很多人,陌生人,都可以进入,就不安全。
(3)人进去后,再锁住(线程进去后再锁定),那么在外面的很多陌生人就进不去。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TEQpWFVx-1615884417568)(./1.png)]
生活当中上厕所

1.1.2synchronized为什么是隐式锁?

因为锁定与解锁,都看不见,也改不了,属于JDK内置锁。

1.1.3锁的分类

(1)内置锁
持有一把锁,(GpsEngine.class对象锁简称类锁)
(2)显示锁

1.1.3.1类锁
package lock;

public class GpsEngine {
    private static GpsEngine gpsEngine;

    public static GpsEngine getGpsEngine(){
        if(null == gpsEngine){
            //Thread-0 CPU执行器被操作系统调度给释放【暂停】【获得CPU执行权 恢复】 继续往下走
            gpsEngine = new GpsEngine();
        }
        return  gpsEngine;
    }

    //如果多个线程或者多个地方调用
    //多线程执行的时候,需要非常注意,安全性问题

    //synchronized:为什么很多开发者都说这是隐式锁?
    // 因为锁定与解锁,都看不见,也改不了,属于JDK内置锁。
    //持有的一把锁,GpsEngine.class对象锁 == 类锁
    public static synchronized GpsEngine getGpsEngine2(){
        if(null == gpsEngine){
            //上锁之后 Thread-0 此时,Thread-1 Thread-2 Thread-3都进不来
            gpsEngine = new GpsEngine();
        }
        return  gpsEngine;
    }//只有thread-0执行完毕 释放锁


    //双重检测
    public static GpsEngine getInstance(){
        if(null == gpsEngine){
            synchronized (GpsEngine.class){
                if(null == gpsEngine){
                    gpsEngine = new GpsEngine();
                }
            }
        }
        return gpsEngine;
    }
}

1.1.3.2对象锁
public class SynTest {
    private long count = 0;


    private static class Count extends Thread{
        private SynTest simplOper;

        public Count(SynTest simplOper){
            this.simplOper = simplOper;
        }

        @Override
        public void run() {
            for (int i = 0 ; i < 10000; i++){
                simplOper.incCount3();
            }
        }
    }

    //累加方法
    //synchronized == 对象锁 持有一把锁,SynTest.this
    private synchronized void incCount1() {
        count ++;
    }


    private void incCount2() {
        synchronized (SynTest.this){
            count ++;
        }
    }

    //随便持有一把锁 对象锁
    private Object object = new Object();
    private String str = new String();
    private  void incCount3(){
        synchronized (str){
            count ++;
        }
    }



    public static void main(String[] args) throws InterruptedException {
        SynTest simpleOper = new SynTest();

        //启动两个线程累加
        Count count1 = new Count(simpleOper);
        Count count2 = new Count(simpleOper);
        count1.start();//累加到10000
        count2.start();//累加到20000

        Thread.sleep(50);

        System.out.println(simpleOper.count);
    }
}
1.1.3.3显示锁
//Lock,显示锁,程序员可控制,锁定与解锁等逻辑
public class LockDemo {

    private int count = 0;
    //显示锁
    private Lock lock = new ReentrantLock();


    public void incr(){
        try {
            //1.开发者自己锁定
            lock.lock();
            //2.如果此句话发生异常,后面的解锁将无法被执行,就没法释放锁,导致其他线程无法进入执行
            count ++;
        }finally {
            //3.开发者自己解锁,放在finally中,确保线程的锁一定得到释放
            lock.unlock();
        }
    }

    private static class Count extends Thread{
        private LockDemo simplOper;

        public Count(LockDemo simplOper){
            this.simplOper = simplOper;
        }

        @Override
        public void run() {
            for (int i = 0 ; i < 10000; i++){
                simplOper.incr();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        LockDemo lockDemo = new LockDemo();
        Count count1 = new Count(lockDemo);
        Count count2 = new Count(lockDemo);
        count1.start();
        count2.start();

        Thread.sleep(50);
        System.out.println(lockDemo.count);
    }
}

1.1.3.3.1可重入锁

(1)ReentrantLock:可重入锁
(2)synchronized:也是可重入锁

    /**
     * 可重入锁就是在递归的时候,可以反复拿到锁
     * 如果synchronized默认就是可重入锁
     * 如果synchronized不是可重入锁,会怎么样?递归将无法拿到锁,造成卡死状态。
     */
    public synchronized void add(){
        count ++;
        add();
    }

1.2多线程之生产者消费者案例一

//容器 资源 描述
public class Res {
    //面包名称
    private String name;
    //编号
    private int id;

    //生产,存入
    public void put(String name){
        id +=1;
        this.name = String.format("%s商品编号:%s",name,id);
        System.out.println(Thread.currentThread().getName() + " ===生产者生产了: " + this.name);
    }

    //消费取出
    public void out(){
        id -= 1;
        System.out.println(Thread.currentThread().getName() + " ===消费者消费了: " + this.name);
    }
}
//生产任务
public class ProduceRunnable implements Runnable{
    private Res res;

    public ProduceRunnable(Res res){
       this.res = res;
    }

    @Override
    public void run() {
        res.put("面包");
    }
}
//消费者任务
public class ConsumeRunnable implements Runnable{
    private Res res;

    public ConsumeRunnable(Res res) {
        this.res = res;
    }

    @Override
    public void run() {
        res.out();
    }
}

/*
1.多线程中,如果没有加锁,基本上都会有安全性问题
 */
public class ThreadCommunicationDemo {

    public static void main(String[] args) {
        //1.创建资源对象
        Res res = new Res();
        //2.创建生产者任务
        ProduceRunnable produceRunnable = new ProduceRunnable(res);
        //3.创建消费者任务
        ConsumeRunnable consumeRunnable = new ConsumeRunnable(res);

        //启动生产者
        new Thread(produceRunnable).start();
        //启动消费者
        new Thread(consumeRunnable).start();
    }

}

1.3多线程之生产者消费者案例二

(1)增加内置锁解决安全问题

//容器 资源 描述
public class Res {
    //面包名称
    private String name;
    //编号
    private int id;

    //生产,存入
    public synchronized void put(String name){
        id +=1;
        this.name = String.format("%s商品编号:%s",name,id);
        System.out.println(Thread.currentThread().getName() + " ===生产者生产了: " + this.id);
    }

    //消费取出
    public synchronized void out(){
        System.out.println(Thread.currentThread().getName() + " ===消费者消费了: " + this.id);
        id -= 1;
    }
}

1.4等待唤醒机制原理

/**
 * 描述资源
 */
class Res3 {
    private int     id;
    private String  name;
    /**
     * 定义标记目的是为了默认先运行生产者,先生产,后消费
     */
    private boolean flag;

    public synchronized void put(String name) throws InterruptedException {
        //生产之前判断标记
        if (!flag) {
            //1.开始生产
            id += 1; System.out.println(String.format("%s 生产者生产了 %d", Thread
                    .currentThread()
                    .getName(), this.id));
            //2.生产完成
            flag = true;

            //3.唤醒 wait()线程,即唤醒消费线程消费,被冻结的线程,如果没有冻结任何线程,没有任何关系,Java默认不会报错
            //3.1注意:wait或者notify必须要有锁包裹着。
            notify();
            //3.2当前自己线程会被冻结掉,释放CPU执行资格,释放CPU执行权,这个时候,CPU会去执行其他线程了。
            wait();
        }
    }


    public synchronized void out() throws InterruptedException {
        if (flag) {
            //1.开始消费
            System.out.println(String.format("%s 消费者消费了 %d", Thread
                    .currentThread()
                    .getName(), this.id));
            //2.消费完成
            flag = false;
            //3.唤醒wait()线程
            notify();
            //4.当前自己线程冻结
            wait();
        }
    }
}


class ProduceRunnable implements Runnable {
    private Res3 res;

    public ProduceRunnable(Res3 res) {
        this.res = res;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 20; i++) {
                res.put("面包");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


class ConsumeRunable implements Runnable {
    private Res3 res;

    public ConsumeRunable(Res3 res) {
        this.res = res;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 20; i++) {
                res.out();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class ThreadCommunicationDemo {
    public static void main(String[] args) {
        Res3 res = new Res3();
        ProduceRunnable p = new ProduceRunnable(res);
        ConsumeRunable c = new ConsumeRunable(res);
        new Thread(p).start();
        new Thread(c).start();
    }
}

等待区域:wait();
需要获取对象的锁

sync(持有的锁 对象的锁){
wait();
}

通知区域:notify()
获取对象的锁
sync(持有的锁 对象的锁){
notify()
}

注意:wait与notify持有的锁是同一把锁。

唤醒目标明确的时候就使用notify();不明确就使用notifiyAll();

1.5ThreadLocal的使用

线程的隔离Handler

(1)多个线程操作ThreadLocal对象,类似于只是对ThreadLocal中操作对象(Person)的复制本做操作,以此避免多个线程同时操作一共享对象时,引发的线程安全问题。
源码层的设计是每个线程去获取值,只是以当前线程作为key,去获取一份儿与当前线程对应的Value.对这个ThreadLocal对象做操作,只是对线程所对应对象做处理,不会影响到其他线程的修改。

(2)案例:

public class ThreadLocalTest2 {
    class Person{
        private String name;
        private String sex;
        private int age;

        public Person() {
        }

        public Person(String name, String sex, int age) {
            this.name = name;
            this.sex = sex;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getSex() {
            return sex;
        }

        public void setSex(String sex) {
            this.sex = sex;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "Person{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + ", age='" + age + '\'' + '}';
        }
    }

    ThreadLocal<Person> threadLocal = new ThreadLocal<Person>(){
        @Override
        protected Person initialValue() {
            Person p = new Person("张三","男",20);
            return p;
        }
    };

    class StudentThread extends Thread{
        @Override
        public void run() {
            String threadName = currentThread().getName();
            System.out.println(String.format("首次获取threadName: %s get: %s",threadName,
                    threadLocal.get()));

            Person p = new Person("王六","男",23);
            threadLocal.set(p);


            System.out.println(String.format("重设之后threadName: %s get: %s",threadName,
                    threadLocal.get()));
        }
    }

    class TeacherThread extends Thread{
        @Override
        public void run() {
            String threadName = currentThread().getName();
            System.out.println(String.format("首次获取threadName: %s get: %s",threadName,
                    threadLocal.get()));

            Person p = new Person("李雪","女",21);
            threadLocal.set(p);


            System.out.println(String.format("重设之后threadName: %s get: %s",threadName,
                    threadLocal.get()));
        }
    }

    public static void main(String[] args) {
        ThreadLocalTest2 outer = new ThreadLocalTest2();
        outer.new StudentThread().start();
        outer.new TeacherThread().start();

        System.out.println(String.format("主线程threadName: %s get: %s",
                Thread.currentThread().getName(),
                outer.threadLocal.get()));
    }
}

(3)执行结果
首次获取threadName: Thread-0 get: Person{name=‘张三’, sex=‘男’, age=‘20’}
首次获取threadName: Thread-1 get: Person{name=‘张三’, sex=‘男’, age=‘20’}
重设之后threadName: Thread-0 get: Person{name=‘王六’, sex=‘男’, age=‘23’}
重设之后threadName: Thread-1 get: Person{name=‘李雪’, sex=‘女’, age=‘21’}

2.打赏鼓励

感谢您的细心阅读,您的鼓励是我写作的不竭动力!!!

2.1微信打赏

在这里插入图片描述

2.2支付宝打赏

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值