------------------------------------多线程----------------------------------
多线程:
0.
多线程的概念:
多线程是这样一种机制,它允许在程序中并发执行多个线程,且每个线程彼此间互相独立。
并发的理解:
多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。
1.
实现线程的方式有两种:
1、继承java.lang.Thread,并重写它的run()方法,将线程的执行主体放入其中。
2、实现java.lang.Runnable接口,实现它的run()方法,并将线程的执行主体放入其中。
==>多线程的执行逻辑:
当主线程被挂起时, 其它就绪的线程会根据选择最高优先级的来执行;
当主线程的挂起时间 > 子线程的执行时间时,子线程执行完后回到主线程,等待主线程醒来.
当主线程的挂起时间 < 子线程的执行时间时,主线程挂起时间到的,自动醒来,回到主线程,此时可以判断子线程是否存在,若有,可stop之.
上面两种实现线程的方式在启动时会有所不同。
# ThreadTest tt = new ThreadTest();
# // 启动线程
# tt.start();
# // 创建一个线程实例
# Thread t = new Thread(new RunnableTest());
# // 启动线程
# t.start();
2.
线程状态的具体信息如下:
1. NEW(新建状态、初始化状态):线程对象已经被创建,但是还没有被启动时的状态。这段时间就是在我们调用new命令之后,调用start()方法之前。
2. RUNNABLE(可运行状态、就绪状态):在我们调用了线程的start()方法之后线程所处的状态。
3. BLOCKED(阻塞状态、被中断运行):
4.TERMINATED(死亡状态、终止状态):线程完成执行后的状态。或run()在运行过程中抛出了一个异常,而这个异常没有被程序捕获,导致这个线程异常终止进入TERMINATED状态。
3.
你可以调用 Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。
4.
同步synchronized
重点理解:
synchronized 方法,用来控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞;方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放;此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态.
也就是说:
线程在执行同步方法时是具有排它性的。当任意一个线程进入到一个对象的任意一个同步方法时,这个对象的所有同步方法都被锁定了,在此期间,其他任何线程都不能访问这个对象的任意一个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导致它释放了该对象的同步锁之后。在一个对象被某个线程锁定之后,其他线程是可以访问这个对象的所有非同步方法的。
在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
同步块也一样:
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
基本格式:
同步方法:
public void methodAAA()
{
synchronized (this) // (1)
{
//…..
}
}
同步块:
public void methodAAA()
{
synchronized (this) // (1)
{
//…..
}
}
实例如下:
同步方法:
public class Thread1 implements Runnable {
int num=100;
public synchronized void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+ " 's num is " + num--);
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}
同步块:
public class Thread1 implements Runnable {
int num = 100;
public void run() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+ " 's num is " + num--);
}
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}
添加了同步后,结果:
A 's num is 100
A 's num is 99
A 's num is 98
A 's num is 97
A 's num is 96
B 's num is 95
B 's num is 94
B 's num is 93
B 's num is 92
B 's num is 91
若不添加同步:
A 's num is 100
A 's num is 98
A 's num is 97
A 's num is 96
A 's num is 95
B 's num is 99 --此处
B 's num is 94
B 's num is 93
B 's num is 92
B 's num is 91
说明:
则线程A正在处理的中间数据若结果数据(99),将线程B中调用了;A又调用了线程B的中间数据,继续计算.
注意:
在定义接口方法时不能使用synchronized关键字。
构造方法不能使用synchronized关键字,但可以使用下节要讨论的synchronized块来进行同步。
3.
Lock是一个接口,它位于Java 5.0新增的java.utils.concurrent包的子包locks中。实现Lock接口的类具有与synchronized关键字同样的功能,但是它更加强大一些。java.utils.concurrent.locks.ReentrantLock是较常用的实现了Lock接口的类。上面的实例可以变为:
public class Thread1 implements Runnable {
int num = 100;
private Lock lock = new ReentrantLock();
public void run() {
try {
lock.lock();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+ " 's num is " + num--);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}
lock ()方法用于锁定对象,unlock()方法用于释放对对象的锁定,他们都是在Lock接口中定义的方法。位于这两个方法之间的代码在被执行时,效果等同于被放在synchronized同步块中。一般用法是将需要在lock()和unlock()方法之间执行的代码放在try{}块中,并且在 finally{}块中调用unlock()方法,这样就可以保证即使在执行代码抛出异常的情况下,对象的锁也总是会被释放,否则的话就会为死锁的产生增加可能。
注意:
引入了锁,但是锁的引入常常会造成一个很大的问题——死锁 。
死锁就是一个进程中的每个线程都在等待这个进程中的其他线程释放所占用的资源,从而导致所有线程都无法继续执行的情况。