接上文继续学习。
四:线程的生命周期:
由上图可以看出,一个线程由出生到死亡分为五个阶段:
1).创建状态
?当用new操作符创建一个新的线程对象时,该线程处于创建状态。
?处于创建状态的线程只是一个空的线程对象,系统不为它分配资源
2). 可运行状态
?执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体—run()方法,这样就使得该线程处于可运行( Runnable )状态。
?这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行。
3).不可运行状态
.当发生下列事件时,处于运行状态的线程会转入到不可运行状态。
调用了sleep()方法;
?线程调用wait方法等待特定条件的满足
?线程输入/输出阻塞
4)返回可运行状态:
?处于睡眠状态的线程在指定的时间过去后
?如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变
?如果线程是因为输入/输出阻塞,等待输入/输出完成
5). 消亡状态
当线程的run方法执行结束后,该线程自然消亡。
注意:
1.停止线程的方式:不能使用Thread类的stop方法来终止线程的执行。一般要设定一个变量,在run方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束。
2.不能依靠线程的优先级来决定线程的执行顺序。
五:多线程并发
多线程并发是线程同步中比较常见的现象,java多线程为了避免多线程并发解决多线程共享数据同步问题提供了synchronized关键字
synchronized关键字:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
1.Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。
2. 如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。
3.如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。
4. synchronized块,写法:
synchronized(object) { }
表示线程在执行的时候会对object对象上锁。
5.synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的代码是可以被多个线程同时访问到的。
同步的线程状态图:
代码实现
public static void main(String[] args) { new TraditionalThreadSynchronized().init();// } private void init() { final Outputer outputer = new Outputer();// new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } outputer.output("1");// } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } outputer.output3("2"); } } }).start(); } static class Outputer { public void output(String name) { int len = name.length(); synchronized (Outputer.class) { for (int i = 0; i < len; i++) { System.out.println(name.charAt(i)); } System.out.println(); } } public synchronized void output2(String name) { int len = name.length(); synchronized (this) { for (int i = 0; i < len; i++) { System.out.println(name.charAt(i)); } System.out.println(); } } public static synchronized void output3(String name) { int len = name.length(); for (int i = 0; i < len; i++) { System.out.println(name.charAt(i)); } System.out.println(); } }
Synchronized是java语言级别内置的同步机制。Synchronized的背后机制是Java在语言级上的锁机制:
- 类锁:对于每一个类,自动配套一个类锁,该锁临界量为1
- 实例锁:对于每一个类实例化后的一个对象,自动配套一个对象锁,该锁临界量为1
对于类锁,目前注意到,只能应用到类得某个方法上:
- 静态方法锁:static synchronized method();
对于实例锁,有以下几种方式:
- 非静态方法锁:synchronized method();
- 代码块锁:synchronized{ }或synchronized(this){ }
- 指定类实例锁:synchronized(Object ob){ }
非静态方法锁与代码块锁实质都一样,是对当前所属类得实例加锁。而指定类实例锁,是对括号里的对象ob加锁,这一点很有意思,灵活运用指定类锁可以构建一些稍微负责的并行程序模型。
其中类锁与实例锁各自独立,不相互冲突,一个synchronized 指定锁,要么是类锁,要么是实例锁,是互斥的。
在多线程中,依然是引用日本作者结成浩的《java多线程设计模式》中的例子:
问题:
[java] view plaincopy
- public class Something(){
- public synchronized void isSyncA(){}
- public synchronized void isSyncB(){}
- public static synchronized void cSyncA(){}
- public static synchronized void cSyncB(){}
- }
那么,对于Something类的两个实例a与b,那么下列组方法何以被1个以上线程同时访问呢
- x.isSyncA()与x.isSyncB()
- x.isSyncA()与y.isSyncA()
- x.cSyncA()与y.cSyncB()
- x.isSyncA()与Something.cSyncA()
:
- 都是对同一个实例的synchronized域访问,因此不能被同时访问
- 是针对不同实例的,因此可以同时被访问
- 因为是static synchronized,所以不同实例之间仍然会被限制,相当于Something.isSyncA()与 Something.isSyncB()了,因此不能被同时访问。
- 能够同时访问,因为一个是实例锁,一个是类锁。
总结
synchronized的语言级别锁机制,让并发项目时,无需增加锁控制类,直接本身原生态支持,十分方便,但注意到,synchronized锁只有两个粒度,一个是类锁,一个是实例锁,而且临界区互斥量为1,对于有些需要临界区大于1的,锁粒度更大,动态调控,那synchronized就不够用了。得需要专门的信号量等机制了。
最后一点是synchronized关键词不能被直接继承,子类在继承负责,覆盖父类的synchronized方法时候,必须同样显示用synchronized关键词。而子类如果不覆盖父类synchronized关键词的非静态方法,直接继承并使用父类的非静态方法时,父类的synchronized字段在子类对象仍然是有效的。