---------------------- ASP.Net+Unity开发、 .Net培训、期待与您交流! ----------------------
java基础——多线程1、进程和线程:
所谓进程,就是正在执行的程序。每个进程执行都有一个顺序,该顺序称为一个执行路径或称为一个控制单元。线程就是进程中的一个独立的控制单元,线程在控制着进程的执行。一个进程中至少有一个线程。
java虚拟机启动时,会有一个进程“java.exe”。该进程中至少有一个线程负责java程序执行,而且这个线程的运行代码存在于main函数中,该线程被称为主线程。值得注意的是,java虚拟机启动时,不止一个线程,严格来说,应该至少有两个线程,一个是主线程,一个是垃圾回收机制的线程。
2、线程的创建方式:
(1)继承Thread类,覆盖run方法,调用线程的start方法启动线程。start方法的作用是,启动该线程并调用run方法。
创建方式:NewThread extends Thread
new NewThread().start();
(2)实现Runnable接口,实现接口中的run方法,通过Thread类,建立线程对象,将Runnable的子类对象作为一个实际参数传递给Thread的构造方法,调用Thread类的start方法,开启线程,并执行Runnable子类的run方法。这样做的原因是,run方法所属的对象是Runnable的子类对象,创建的线程要去执行这个run方法,那么,就必须明确该run方法所属的对象。
创建方式:NewRunnable implements Runnable
new Thread(new NewRunnable()).start();
(3)这两种创建方式的区别:实现接口的方式,线程的执行代码存放在接口子类的run方法中;继承Thread的方式,线程的执行代码存放在Thread子类的run方法中。实现接口的方式,避免了单继承的局限性。建议使用实现接口的方式创建线程。
3、run和start的区别:
run方法:在Thread类中就定义了一个功能,就是run方法。这个方法用于存储线程要运行的代码。
new Thread().start():创建线程,启动线程,调用线程的run方法
new Thread().run():创建线程,使用Thread对象调用run方法,只是普通的对象调用方法,线程并未启动。
4、多线程的运行机制
多线程在运行时,我们往往认为是多个线程在同时运行,其实不然。因为单核CPU在某一时刻,只能执行一个线程。多个线程都在获取CPU的执行权,CPU执行到谁,谁就运行。CPU在做着高速的切换,以达到同时运行的假象。我们可以形象的把多线程的运行,看作是在互相抢夺CPU的执行权。我们在执行多线程时,发现每一次的结果都有可能不一样,这就是多线程的一个特性——随机性。哪个线程“抢到”了CPU的执行权,那个就执行,至于执行多久,CPU说了算。
5、线程的状态:
(1)被创建:new Thread() 或 new Thread(Runnable)
(2)运行:在被创建状态调用start方法,达到运行状态;
(3)冻结:冻结状态又分为睡眠和等待两种状态。在运行状态执行sleep(时间)方法,会使线程进入睡眠状态,没有执行资格,直到睡眠时间到,这是可能会进入阻塞状态,或进入运行状态,由CPU决定。另外一种,在运行状态执行wait()方法时,会是进程进入等待状态,没有执行资格,直到执行了notify()方法将其唤醒,进入阻塞或运行状态。
(4)阻塞:阻塞状态是介于运行状态和冻结状态的一个临时状态;
(5)消亡:run()方法执行结束,或执行了stop()方法。
图解:
6、获取和设置线程对象的名称:
每个线程都有自己默认的名称“Thread-编号”,编号从0开始。在Thread类中,有getName()这个方法,用于获取线程的名称。setName(Name)用于自定义线程的名称。另一种自定义线程名称的方式是,使用Thread的构造方法Thread(String name).
①Thread t = new Thread("t1")
②Thread t = new Thread(Runnable,"t1")
Thread.currentThread():获取当前线程对象,所以通常情况下会使用,Thread.cunrrentThread().getName()这种方式来获取当前线程的名称。
7、线程安全
有一个共享数据,有多条语句在操作它,当一个线程运行时,没有全部执行完这些操作语句时,它的执行权就被另一个线程获取,这种情况,就会导致共享数据出现错误。就出现了线程安全问题。如何解决这个问题呢?通过思考我们知道,针对共享数据,每一次只能由一个线程来操作。就是一个线程全部操作完后,其他线程才可以操作。这就是使线程同步。
java中,针对线程安全问题提供了专业的处理——线程同步。关键字:synchronized(对象锁),括号中的对象锁的作用就是,避免多个线程同时进入执行。
它的原理:就像门上的一把锁,当某个人获得钥匙后,进入房间,从里面反锁,别人即使得到了钥匙,也进不了门;直到里面的人出来,别人才能进去。
这个关键字可以作为一个修饰符,修饰方法,被修饰的方法,就是一个线程同步的方法。如果修饰的是一个非静态的方法,那么它的锁就是该类的对象;如果修饰的是一个静态方法,那么它的锁就是该方法所在类的字节码文件对象(类名.class)。这个关键字也可以修饰代码块,被修饰的就是同步代码块。格式与修饰方法不同,必须指定锁。如下:
修饰方法:
public synchronized void show(){} //锁是该类的对象(this)
public static synchronized void show(){} //锁是(类名.class)
修饰代码块:
synchronized(对象锁){ 同步代码块 }
同步的前提:①有两个或两个以上的线程;②多个线程必须使用同一个锁。
同步线程的好处,就是解决的线程安全问题。但也有弊端,多个线程在执行时,需要判断锁,较耗资源。
如何判断哪些代码该同步,哪些代码不该同步?
①明确哪些代码是多线程运行的代码;
②明确共享数据;
③明确多线程运行代码中,哪些语句是操作共享数据的。
通过对多线程的了解,我们发现“懒汉式”单例模式,存在线程安全问题,在多线程中使用时,需要处理一下:
public static Single getInstance()
{
if(s==null)//之所以多加这一层,是减少多线程对锁的判断。
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
}
通过上面的处理后,就解决了线程安全问题。
在做线程同步处理的时候,有可能会出现死锁。一个共享资源,被多种方式操作,如一个方法或代码块,如果同步的锁是不一样的,当两个线程访问该资源时,就有可能会出现死锁。死锁的例子:
public void run(){
if(flag){
synchronized(MyLock.locka){ //同步的锁是MyLock.locka
System.out.println("if locka");
synchronized(MyLock.lockb){ //同步的锁是MyLock.lockb
System.out.println("if lockb");
}
}
}else{
synchronized(MyLock.lockb){ //同步的锁是MyLock.lockb
System.out.println("else lockb");
synchronized(MyLock.locka){ //同步的锁是MyLock.locka
System.out.println("else locka");
}
}
}
}
在上面这个例子中,两种访问情况使用了不同的锁,一个拿了一个锁。就相当于两个人一人拿了一支筷子,谁都吃不上饭。但即使是上面这种情况,也会出现和谐的情况,正常输出。
在开发过程中,我们应当避免死锁发生,也就是尽量避免使用同步嵌套。对同一个资源的同步操作,一定要使用相同的锁。
---------------------- ASP.Net+Unity开发、 .Net培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net