------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
首先感谢黑马、感谢素不相识的 毕老师
一、多线程理解
a、 进程
程序正在计算机中运行我们称之为进程。
进程不是乱序执行的,都是按照一定的order开展的。我们称这个order为一个控制单元。
b、线程
正在运行的进程的一个控制单元就叫做一个线程。线程(控制单元)在操纵着该进程运行。如果存在某个控制单元还在运行,那么这个进程还会继续运转,线程对应进程是一对多的关系。
c、多线程
当我们打开windows的任务管理器监控进程时会发现,当jvm一启动,就会多出一个java.exe的图标。Java。Exe会有一个控制单元来掌控java程序的运行,这就是main Thread。这就是我们平时所讲的主线程。但不要忘了这个线程在执行的过程中还有另外的一个控制单元在运行,我们知道java不同于c++,它制定了一个垃圾自动回收的机制,这个回收垃圾的线程就是和main Thread 同时进行的另外的一个控制单元,这个就是所谓的多线程。它的创建能够使得进程不间断地运行这样一来提高了cpu的执行效率,进而提高了计算机的工作效率。多线程还有一个特点就是随机性,不同的进程都在“抢”资源,但最终谁能取得运行权实际上是由CPU决定的。
二、线程的建立方式的两种主要方法
One、 继承(extends)的方式
Thread类 ---是java工程师们设计的对线程class表达的类,只要创建子类,让后覆盖父类中德run()函数,然后实例化这个子函数就创建了一个线程。下面我们了解一下创建的过程。
具体步骤:定义一个类extends Thread----〉覆盖Thread类中run函数来实现自己的定义的功能--------〉实例化一个子类对象,这时一个线程已经创建完成----->该实例对象调用线程的start()方法,线程启动。(这需要说明的两点run方法和start方法:run方法是用来定义线程要执行的代码块,而创建了线程,线程并没有执行只有用start方法后方才执行)
(毕老师教学代码extends Thread)
class Demo extends Thread
{
public void run()
{
for(int x=0; x<60; x++)
System.out.println("demo run----"+x);
}
}
class ThreadDemo
{
public static void main(String[] args)
{
//for(int x=0; x<4000; x++)
//System.out.println("Hello World!");
Demo d = new Demo();//创建好一个线程。
//d.start();//开启线程并执行该线程的run方法。
d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。
for(int x=0; x<60; x++)
System.out.println("Hello World!--"+x);
}
}
One、 实现(implements)的方式
Runnable是一个特殊的接口:通过实现这个接口来完成线程的创建
具体步骤:
创建一个子类implements Runnable接口--〉重写该接口的抽象方法run,将要运行的代码块存入run方法中---〉创建一个新的Thread线程对象----》将Runable子类的实例对象作为实参传递给Thread的构造方法(这时只要让线程开始执行对象的覆盖接口的run方法即可)---〉这时调用线程的start方法启动线程并调用run方法
(毕老师教学代码implements Runnable)
class Ticket implements Runnable
{
private static int tick = 100;
//Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(Ticket.class)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
}
}
}
}
else
while(true)
show();
}
public static synchronized void show()
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
}
}
}
class StaticMethodDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
t2.start();
}
}
二者的最大的区别在于 继承的方式 的run函数是在Thread里面运行,而实现的方法中run()方法是在接口的子类中运行
三 线程的状态分析
New Thread: 创建线程后 等待启动
Strart: 启动运行线程,这里一定要明白这运行状态时的两种情况(获得执行权和执行权):对于后期理解线程出现的问题很有必等待状态:就是获得了执行资格,不过没有执行权。
sleep(time)和wait():冻结状态
notify():唤醒方法,从新获得执行资格,转为临时状态。
停止状态:stop()方法,以及run方法结束。
。
四、线程安全问题
1、导致安全问题的出现的原因:
共享数据被多个线程执行的时候很容易导致错误的产生
一般包括两种情况
1、不同的线程访问时由于执行权和执行资格的获取不同 导致停滞状态。
b、多线程执行的随机性 。
值得注意的是:线程安全问题在测试的时候,有时很难发现,在实际应用中会才会产生异常。
2、安全问题的破解——同步
共享数据被多个线程执行的时候,有且只有一个线程执行任务。别的线程不允许执行。synchronized就很好的满足了上述要求(同步代码块、同步函数两种方法来实现)
a、同步代码块
class Res
{
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name,String sex)
{
if(flag)
try{this.wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(name+"........"+sex);
flag = false;
this.notify();
}
}
class Input implements Runnable
{
private Res r ;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
if(x==0)
r.set("mike","man");
else
r.set("丽丽","女女女女女");
x = (x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r ;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class InputOutputDemo2
{
public static void main(String[] args)
{
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
/*
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
*/
}
}
五、注意的几个问题:
同步的前提:
a.有多个线程。
b.都是用的同一个锁。并且同步中有且只有一个线程在执行。
加锁好处:解决多线程的安全问题。
加锁弊端;多个线程都需要判断锁,所有运行较慢。
注:同步函数static修饰后,同步的对象是该类所属的字节码class
wait(),sleep()二者的显著区别:
wait():放弃cpu执行权,放锁。
sleep():放弃cpu执行权,不放锁。