黑马程序员:java学习要点-多线程(上)

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

1. 多线程

先面向对象,然后各对象的具体使用

1.1. 多线程技术——解决问题

进程:正在进行中的程序。是一个应用程序在内存中的一篇内存空间

线程:负责程序的执行,一个进程中至少有一个线程

如果有多个,这个程序就称为多线程

线程也可以称为执行线程,执行路径,执行情景

多线程的好处:可以让多部分代码同时执行

提高用户体验

???多线程之间的通信???

虚拟机的底层原理:

JVM多线程的基本体现:JVM也是多线程,至少有一个主线程和一个垃圾回收线程。主线程执行主函数中的内容,垃圾回收线程执行的是对堆内存进行内存释放的代码。

如何创建一个自定义的线程呢?

两种方式:

1,继承Thread类,复写run方法

原因:创建线程是为了开辟一个执行路径去执行指定的代码,让其和其它代码同时执行。意味着每一个线程创建都有一个自己要运行的内容,而这个内容也称为线程的任务。

而线程类在描述线程时,要执行什么任务,线程自己最清楚

所以他有一个功能函数,专门用于封装要执行的任务,这就是run方法

主线程也是同理,该线程执行的任务都在主函数中

由于任务内容要我们自己来指定,所以我们要重写run方法,能重写就是说必须是Thread的子类

完了之后,建立对象,调用start方法开启线程,执行run方法

每一个线程对应一个栈分区

多个线程之间不影响,一个线程发生异常,不影响其他线程的继续运行

, 2,创建线程的第一种方式是:

1,继承Thread

2,复写run方法,把线程要执行的人物代码存储到run方法中

3,创建Thread子类的对象

4,调用start方法开启线程并调用run方法

3,线程调用start方法和调用run方法的区别?

调用run方法,并没有开启线程,都是主线程来执行,无法完成同步运行

调用start方法,开启线程,并调用了线程的任务方法run方法,实现代码的同步执行

1.2. 线程的四种状态

NEW

A thread that has not yet started is in this state.

A thread executing in the Java virtual machine is in this state.

A thread that is blocked waiting for a monitor lock is in this state.

A thread that is waiting indefinitely for another thread to perform a particular action is in this state.

A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.

A thread that has exited is in this state.


1.3. 创建多线程的方式二——实现Runnable接口

1.3.1. 步骤:

1,定义类实现Runnable接口

2,覆盖该接口中run方法

3,通过Thread类创建对象

4,将实现Runnable接口的实现类的对象作为参数传递给Thread类的构造函数

run方法所属的对象传递给线程,让线程去运行指定的对象之中的run方法

5,调用Thread类的start方法开启线程

1.3.2. 与第一种方式的区别

一块代码想要被同时执行,这时需要多线程来完成

第一种方式的要点在于继承Thread-extends Thread,拥有自己run方法和自己的start方法;而继承的弊端就在于无法多继承

第二种方式的要点在于实现Runnable接口-implements Runnable,得到功能的扩展,拥有自己的run方法,然后把自己的对象传递给Thread的对象,用Threadstart开启线程

——————Thread使用该接口,其他实现该接口

???为什么说Thread使用了Runnable接口

——Thread的构造方法

???统计两种方式的工作量???耦合性是不是必须的!

线程实际运行时,都是一个线程对应一个线程的功能

一对一就是耦合性啊。运行的时候线程跟线程功能就是一一对应的

两种方式都一样啊???!!!

Runnable接口出现的好处:

1,避免了单继承的局限性

2Runnable接口的出现,更是按照面向对象的思想,将线程要运行的任务进行单独的对象的封装。降低了线程对象和线程任务的耦合性

//将线程任务进行描述

class Demo implements Runnable

{

public void run(){}

}

class Demo2 implements Runnable

{

public void run(){}

}

Demo d=new Demo();//将线程任务封装成对象

Demo2 d1=new Demo2();

Thread t=new Thread(d);//通过线程对象的构造函数和任务相关联

创建线程,建议使用第二种方式!

多线程执行时候的内存图:


1.3.3. 多线程的安全问题

对安全问题的分析:

1,多个线程在处理同一个共享数据

2,线程任务代码中有多条语句在操作这个共享数据

3,这些多条操作共享数据的语句被多个线程分开进行执行

也就是说:在一个线程执行这些语句的过程中,其他线程参与了运算导致数据错误

总结:

线程任务代码中只要存在着

1,共享数据

2,多条操作共享数据的语句

容易产生多线程的安全问题。

1.3.4. 解决方案:

将多条操作共享数据代码进行封装

在某一个时刻,只能有一个线程在内运行,运行期间其他线程都进不来

只有这个线程运行完,其他线程才有机会执行其中的内容

1.3.5. synchronized

java中提供了用于封装的代码块,就是同步代码块。

synchronized(obj){}//这个obj对象可以是任意的,相当于锁

——该代码块之中每个时刻只有一个线程在运行

当程序中出现了多线程的安全问题时,加入了同步进行解决,却发现问题依旧!!!

注意同步的前提:

必须有多个线程,而且使用的是同一个锁!——(在堆之中,保证锁的唯一)

同步原理:

线程0执行同步代码块,拿到对象obj,开始执行内部语句,小睡10msCPU切换到了线程3,线程3开始执行同步代码块,判断obj的所有权,线程3不能拿到obj,不能执行内部语句;CPU切换到线程1,判断obj的所有权,没有拿到obj,不能执行内部语句;CPU切换回线程0,继续执行小睡之后的语句,同步代码块执行完之后,释放objCPU继续切到线程2,线程2开始执行同步代码块,线程2拿到obj,执行内部语句............

同步的弊端:

降低了效率。CPU的很多切换后该线程没有拿到对象无法执行,做了很多无用功!

侧重安全,则使用同步;

1.3.6. 同步函数,同步代码块

如果函数之中的内容都是需要同步的代码块,则可以在函数上加上synchronized关键字,成为同步函数

同步函数用的是哪个锁呢?

同步函数使用的是当前对象this,所以同步函数并没有直接声明锁对象,而是使用当前this

静态同步函数使用的是哪个锁呢?

静态所属于类,任何一个类被使用,装载进内存

都会被封装成一个对象。也称为字节码文件对象

该字节码文件对象的表示方式:对象.getClass()----------(this.getClass());

另一种方式——类名.Class  如同直接写一个字节码文件名一样

同步函数和同步代码块的区别:?????

同步函数使用的锁对象是固定的

同步代码块使用的锁对象是任意的

建议使用同步代码块——尤其是在代码中使用多个同步不同锁时,必须使用同步代码块

//固定的含义就是不受控制的,任意的含义就是可以自己指定的----是可控的

不受控的在某些时候是不适用的

如,相同的地方,有多个同步,就想使用不一样的锁,如果使用同步函数,比较难实现,因为调用者有可能一样——也就是对应相同的锁;或者几个同步都是静态的,那使用的锁也就是相同的。

1.3.7. 单例设计模式——

//饿汉式

class Single

{

private static final Single s=new Single();

private Single(){}

public static Single getInstance()

{

return s;

}

}

//懒汉式:

/*

在被多线程并发访问的时候,会引发多线程的安全问题,无法保证多线程饿唯一性

在获取对象的方法上加上同步关键字,让该方法具备同步性,就可以解决线程安全问题

但是,性能会降低

解决性能问题的方式:

写同步代码块,通过双重判断的形式

这样做的好处就是减少了判断锁的动作,相对提高了性能。

*/

class Single

{

private static Single s=null;

private Single(){};

public static synchronized Single getInstance()

{

if (s==null)

{

s=new Single();

}

return s;

/*

if (s==null)

{

synchronized(Single.class)//???第一次等待队列中有几个线程就判断几次

{ //之后的线程进来之后就不再判断锁对象

if (s==null)

{

s=new Single();

}

return s;

}

}

return s;

*/

}

}

class  SingleDemo

{

public static void main(String[] args) 

{

System.out.println();

}

}

1.3.8. 死锁

/*

死锁:

常见情况之一:同步的嵌套。同步中还有同步,但是锁不同——锁嵌套

两个线程:一个是运行在flagtrue的同步代码块中,

一个是运行在flagfalse的同步函数中

为了实现同步嵌套,让同步代码块中有同步函数,同步函数中有同步代码块

*/

class Test implements Runnable

{

private boolean flag;

Test(boolean flag)

{

this.flag=flag;

}

public void run()

{

if (flag)

{

while (true)

{

synchronized(MyLock.locka)

{

System.out.println("if__a");

synchronized(MyLock.lockb)

{

System.out.println("if__b");

}

}

}

}

else

{

while (true)

{

synchronized(MyLock.lockb)//MyLock.locka的问题????

{

System.out.println("else__b");

synchronized(MyLock.locka)

{

System.out.println("else__a");

}

}

}

}

}

}

class MyLock

{

public static final Object locka=new Object();

public static final Object lockb=new Object();

}

class DeadLock 

{

public static void main(String[] args) 

{

Test t1=new Test(true);

Test t2=new Test(false);

Thread c1=new Thread(t1);

Thread c2=new Thread(t2);

c1.start();

//try{Thread.sleep(1);}catch(InterruptedException e){}

c2.start();

}

}

写在后面:

这段时间下来,感觉每节课都能拿住大的原理,但是小的细节遗忘的很快。每天回来总结完今天的内容,想预习明天的,又想复习昨天的,时间真不够用啊!

白天得提高效率,晚上尽量早点睡!身体已经疲惫不堪了,靠着精神强撑着也不是办法啊!注意保护身体啊,未来的路还很长~~~本钱不能先没了!

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值