----------- 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. 线程的四种状态
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的对象,用Thread的start开启线程
——————Thread使用该接口,其他实现该接口
???为什么说Thread使用了Runnable接口
——Thread的构造方法
???统计两种方式的工作量???耦合性是不是必须的!
线程实际运行时,都是一个线程对应一个线程的功能
一对一就是耦合性啊。运行的时候线程跟线程功能就是一一对应的
两种方式都一样啊???!!!
Runnable接口出现的好处:
1,避免了单继承的局限性
2,Runnable接口的出现,更是按照面向对象的思想,将线程要运行的任务进行单独的对象的封装。降低了线程对象和线程任务的耦合性
//将线程任务进行描述
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,开始执行内部语句,小睡10ms;CPU切换到了线程3,线程3开始执行同步代码块,判断obj的所有权,线程3不能拿到obj,不能执行内部语句;CPU切换到线程1,判断obj的所有权,没有拿到obj,不能执行内部语句;CPU切换回线程0,继续执行小睡之后的语句,同步代码块执行完之后,释放obj;CPU继续切到线程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. 死锁
/*
死锁:
常见情况之一:同步的嵌套。同步中还有同步,但是锁不同——锁嵌套
两个线程:一个是运行在flag为true的同步代码块中,
一个是运行在flag为false的同步函数中
为了实现同步嵌套,让同步代码块中有同步函数,同步函数中有同步代码块
*/
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();
}
}