#Zeroから始めるJava#Java学习笔记(2020/2/15)

学习到多线程了,感觉多线程的目的是为了实现并发,让程序不是鱼贯而入,而是齐车并驾。

一、如何实现多线程程序

①:继承Thread类,重写run方法设置线程任务,使用strat方法启动线程。

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName());   //打印当前线程名字
        }
    }
=====================================================================================
public class MyThreadDemo {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();    //开启线程任务
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName());
        }

    }
}

②:实现Runnable接口,重写run方法,然后在main方法中创建Thread对象,把Runnable对象作为构造方法参数。再使用Thread对象的start方法开启线程。
\color{red}{Runnable接口是没有start方法的,所以要用Thread类中的start方法开启线程}

public class RunnableImpl implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"----"+i);
        }
    }
}
=====================================================================================
public class RunnableDemo {
    public static void main(String[] args) {
        RunnableImpl runnable = new RunnableImpl();
        Thread th = new Thread(runnable);
        th.start();
        for (int i = 0; i < 60; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
}

Runnable接口实现多线程的好处:
1.消除了继承的局限,若用继承Thread来实现多线程,那么该类就不能继承其他类了,因为类是单继承的
2.提高耦合性,每个实现Runnable接口的实现类,可以定义不一样的线程任务,而不是有需求变更就去改Thread类的子类,把设置线程任务和开启新线程分开了。
Runnable实现类——>设置线程任务
Thread对象——>开启新线程

二、线程安全问题

1.synchronized同步

多个线程访问同一资源时,可能出现线程安全问题

比如说,电影院三个线程卖一张票,三个线程同时并发执行到了卖票这个操作,但没有执行到票数减一的操作,此时三个线程都以为还有一张票,却有两个窗口买了两张幽灵票,这就是线程安全问题

解决线程安全问题方法:

①:synchronized代码块:obj是锁对象,作用是,在线程执行到synchronized时获取锁,那么其他线程执行到synchronized时,因为锁已经被获取走了,那么其他进程就进入阻塞状态。

    public void run() {
       Object obj = new Object();
       synchronized (obj){
           //可能会出现线程安全的代码块
       }    
   }

②:synchronized方法:
同步锁是谁? 对于非static方法,同步锁就是this。 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

public class Synchronized extends Thread{
    @Override
    public void run() {
       method();
        
    }
    public synchronized void method(){
            //可能会出现线程安全的代码块
    }
}

2.Lock锁

Lock锁是一个接口,其实现类有:ReentrantLock。
常用方法:
void lock() 获取锁。
void unlock() 试图释放此锁。

当有try/catch处理异常时,我要把unlock放在finally代码块里面,不然如果抛出了异常,那么这个线程就进入死锁了。

三、线程的状态

线程的状态

四、线程等待和线程唤醒

如果线程1必须要线程2完成某项业务后再执行,那么我们可以让线程1进入等待状态,线程2结束某项业务后再唤醒线程1.

无限等待:
Object类中有wait()方法,让当前进程进入等待状态。
void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。


随机单个唤醒:
Object类中有notify()方法,唤醒等待状态的线程。
void notify()
唤醒在此对象监视器上等待的单个线程。
若有多个等待进程,则随机唤醒一个


计时等待:
void wait(long timeout)在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。


全部唤醒:
void notifyAll()
唤醒在此对象监视器上等待的所有线程。

\color{red}{注意点:进行等待唤醒的锁对象(监视器)是必须是唯一的。}

五、使用线程通信——等待唤醒的好处

单纯使用synchronized的话,
资源就只能等拥有锁的线程使用完毕,但是同时其他线程会来尝试竞争资源,但是并没有对象锁,所以只能继续进入阻塞状态。
各线程会不断地去竞争cpu资源,未拥有对象锁的线程就一直做着无用功,造成了不必要的资源浪费。

但是使用wait方法让线程进入等待状态的话,Waiting的线程就不会来争夺cpu资源,不再参与调度,而是老老实实等待有线程去唤醒(notify方法)它

六、线程池

由于线程的创建、销毁是比较占用资源的,所以为了能反复使用线程,有了线程池的概念。


image.png

使用方法:
1.通过线程池工厂中的静态方法设置线程池中的线程数目,并返回一个线程池对象。
static ExecutorService newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。

ExecutorService service = Executors.newFixedThreadPool(2)

2.创建Runnable实现类对象,当然也能用匿名内部类的方法在submit中创建实现类对象。

  MyRunnable r = new MyRunnable();

匿名内部类的方法传递Runnable实现类对象

  service.submit(new Runnable() {
            @Override
            public void run() {
                
            }
        });

3.使用线程池的submit方法,将Runnable实现类对象提交给线程池,然后在线程池中调用线程执行Runnable实现类对象中的run方法

 service.submit(r);

Future<?> submit(Runnable task)
提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。

七、Lambda函数编程

当接口中只有一个方法时,我们可以使用Lambda函数编程来实现接口内的功能,简化了代码。

        //使用匿名内部类实现接口
        Arrays.sort(array, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge()-o2.getAge();
            }
        });
        //使用Lambda表达式实现接口
        Arrays.sort(array,(Person p1,Person p2)->{return p1.getAge()-p2.getAge();});

为了排序, Arrays.sort 方法需要排序规则,即 Comparator 接口的实例,抽象方法 compare 是关键;
为了指定 compare 的方法体,不得不需要 Comparator 接口的实现类;

为了省去定义一个 ComparatorImpl 实现类的麻烦,不得不使用匿名内部类; 必须覆盖重写抽象 compare 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;

实际上,只有参数和方法体才是关键,也就是要做什么才是关键,怎么实现的并不是重点

使用方法是,(参数列表)->{抽象方法的实现}

省略规则 在Lambda标准格式的基础上,使用省略写法的规则为:

1. 小括号内参数的类型可以省略
2. 如果小括号内有且仅有一个参,则小括号可以省略;
3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号

\color{red}{使用函数接口的注意事项:}
\color{red}{接口仅能有一个抽象方法,但是可以有多个非抽象方法}
\color{red}{使用Lambda必须具有上下文推断}
\color{red}{是方法的参数或局部变量类型必须为Lambda对应的接口类}
\color{red}{型,才能使用Lambda作为该接口的实例。}
1.局部变量

 Runnable a = ()->{
            for (int i = start; i <=end ; i++) {
                System.out.println(i);
            }
        };

因为接收变量的类型是Lambda接口,所以Lambda表达式才知道要实现的是Runnable 这个接口。

2.形式参数

new Thread(()->{
            for (int i = start; i <=end ; i++) {
                System.out.println(Thread.currentThread().getName()+"-->"+i);
            }
        }).start();

我们看看Thread方法的源码。
Runnable 是形式参数,也就是说通过形式参数,知道了写的Lambda表达式是Runnable 接口的实现。

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352