----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
一、进程
进程是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程 本身从产生、发展到最终消忙的过程。多进程操作系统能同时运行多个进程(程序),由于CPU具备分时机制,所以每个进程都能循环会的自己的CPU时间片,由于CPU执行速度非常快,使得所有程序好像是在"同时"运行一样。
二、线程
进程在执行过程中,可能需要多个任务同时执行,每个任务的执行者就是线程。线程就是进程中的一个执行控制单元。程的运行时间分派由 CPU来决定,如果多有多个线程,那么CPU也不会有顺序的去执行它们,而是随机的。当一个线程运行一段时间之后,应该主动阻塞,这样CPU就会把资源分派给其他线程。如果线程不合作,自己不主动阻塞,那么CPU会在一段时间之后强行停止这个线程的运行,把资源分派给其他线程。CPU会为每个线程都指定一个时间分片,当时间片用完时,线程还不阻塞,那么就强行停止它。
三、线程的状态
1.新状态
Thread t = new Thread();
刚刚new出来的线程对象,还没有调用start()方法。
2.就绪状态(可运行状态)
t.start()之后中,线程进入就绪状态!
在某一个时间点上,只有一个线程是运行着的,其它的线程都没有运行,所以我们说start()之后 不是“运行状态”,而是“就绪状态”。线程什么时候从就绪运行,这由CPU来决定, CPU会给运行的线程一个时间片,这个时间用完,如果线程还没有主动阻塞,那么CPU会强行停止运行,给其他线程运行机会。
3.运行状态
由CPU决定,CPU会在就绪状态的线程中随机选择一个,给其运行的时间片。运行的线程,应该主动进入阻塞状态,这样给其他线程运行的时间。
4.阻塞状态
休眠:Thread.sleep(1000),当线程执行了sleep()方法,那么这个线程就进入了休眠状态。休眠的线程必须要等到指定的毫秒过完,才能返回到就绪状态。
等待:当前线程执行了wait()方法,进入了对象的等待列表中。只能期待其他线程调用notify()或notifyAll()来唤醒这个等待的线程。
挂起:调用了该线程的suspend()方法,那么这个线程就挂起了。这时就期待该线程的resume()被调用,才能回到就绪状态。但是,这两个方法都被作废了,你不应该使用它们!
IO阻塞:当线程正在完成IO操作,那么这个线程也就阻塞了。直到IO操作完成了!
锁定: 当线程要使用的对象被其他线程使用时,那么这个线程进入了锁定状态。直到线程得到了要使用的对象后,那么就回到就绪状态。
5.死亡状态
run()方法结束,正常死亡!
run()中抛出了异常,因为而死亡!
run()被杀了,有人调用这个线程的stop()方法。stop()方法被作废了!
四、多线程
所谓多线程是指一个进程在执行过程中可以产生多个线程,这些线程可以同时存在、同时运行形成多条执行线索。一个进程可能包含了多个同时执行的。
1、线程的特点:
(1)、线程是指程序的运行流程
(2)、"多线程“的机制是指可以同时运行多个程序块,使程序运行的效率变得更高,也可以克服传统程序语言所无法解决的问题
2.创建线程的两种方式;
继承Thread实例1:
public class Test { public static void main(String[] args) { //创建线程 ThreadTest tt = new ThreadTest(); //开启线程 tt.start(); for (int i = 0; i < 5; i++) { System.out.println("主线程在运行"); } } } //ThreadTest类继承了Thread类,此类实现了多线程 class ThreadTest extends Thread { //重写run方法(必须重写) public void run() { for (int i = 0; i < 5; i++) { System.out.println("次线程在运行"); } } }
结果:
主线程在运行
主线程在运行
次线程在运行
次线程在运行
主线程在运行
主线程在运行
次线程在运行
次线程在运行
总结:1.此类继承了Thread类
2.重写run方法
3.唯一的缺点就是只能单继承
实现接口Runnable实例2:
public class Test { public static void main(String[] args) { // 运用匿名内部类来实现多线程,让外部类继承Thread类,让该内部类实现Runnable接口 new Thread(new Runnable() { // 从写run方法 public void run() { for (int i = 0; i < 5; i++) { System.out.println("002线程在运行"); } } }).start();//线程开启 // 这是main方法线程 for (int i = 0; i < 5; i++) { System.out.println("001线程在运行"); } } }
结果:
结果:
001线程在运行
001线程在运行
002线程在运行
002线程在运行
001线程在运行
001线程在运行
002线程在运行
002线程在运行
总结 1.让外部继承了Thread类
2.让内部类实现了Runnable接口
3.重写run方法
4.创建线程
五、线程同步
同步的目的:用来处理多个线程共享同一数据时,所造成的错误。
如何处理这种情况呢?有三种同步方式
方式1,使用同步块:
它的语法2 synchronized块的语法格式
synchronized(监视器对象) {
……
}
注意:共享数据不可能是局部变量,因为局部变量,都是每个线程都有一份自己的拷贝!共享数据是属性。
public class ThreadDemo { /** * @param args * @throws InterruptedException */ public static void main(String[] args) { //创建线程t110 ThreadaAS t110 = new ThreadaAS(); //创建线程t111 Thread111 t111 = new Thread111(); //开启线程 t110.start(); t111.start(); } } //创建线程aAs,继承了Thread类 class ThreadaAS extends Thread { //重写Thread类的run()方法 public void run() { //输出9个a for (int a = 0; a < 10; a++) { System.out.print("a"); } } } //创建线程111,继承了Thread类 class Thread111 extends Thread { //重写Thread类的run()方法 public void run() { //输出9个b for (int a = 0; a < 10; a++) { System.out.print("b"); } } } //结果:aaaaaabbbbbbbbbbaaaa (没有使用同步块效果)
有同步块的效果synchronized
public class ThreadDemo { /** * @param args * @throws InterruptedException */ public static void main(String[] args) { //创建共享数据 Abc a = new Abc(); //将数据添加进线程t110 ThreadaAS t110 = new ThreadaAS(a); //将数据添加进线程t111 Thread111 t111 = new Thread111(a); //开启线程 t110.start(); t111.start(); } } //创建执行线程ThreadaAs,继承了Thread类 class ThreadaAS extends Thread { //创建类类型引用 private Abc abc; //创建有参构造函数,接受共享数据 public ThreadaAS(Abc abc) { this.abc = abc; } //重写run方法 public void run() { //当线程执行时,锁住CPU执行权,等执行完毕,再释放执行权 synchronized (abc) { abc.fun1(); } } } class Abc { //定义方法1 public void fun() { for (int i = 0; i < 6; i++) { System.out.print("b"); } } //定义方法2 public void fun1() for (int i = 0; i < 6; i++) { System.out.print("a"); } } class Thread111 extends Thread { //创建类类型引用 private Abc abc; //创建有参构造函数,接受共享数据 public Thread111(Abc abc) { super(); this.abc = abc; } //重写run方法 public void run() { //当线程执行时,锁住CPU执行权,等执行完毕,再释放执行权 synchronized (abc) { abc.fun(); } } } 结果:aaaaaabbbbbb
总结:synchronized块的作用,a和b两个线程在同步块内容上同步互斥了,当a进入同步块后,b需要等待a退出后才能进入同步块。
六、单列设计模式的安全问题
相信大家在学单列的时候,都知道饿汉式相对于懒汉式来说安全些,那是什么原因造成懒汉式不安全呢?
下面我写出代码并作出相应的回答,相信大家一看便知
注意:
懒汉式与饿汉式有什么不同?
实例延迟加载,
懒汉式的延迟加载有没有问题?
有,如果多线程访问的时候,会出现安全问题。
怎么解决?
使用同步代码块解决,但是会有些低效。可以使用双重判断解决效率问题。
加同步的时候使用的锁是哪一个?
使用的是该类的字节码文件对象。
实例:
class SingleDemo { // 私有化构造函数,让外界无法new新的对象 private SingleDemo() { } // 创建自己类的对象 private static SingleDemo s = null; // 创建一个新的对象 public void show() { System.out.println("这是懒汉式"); } // 对外定义一个访问的的方法,返回一个对象 public static SingleDemo getSingle() { if (s == null) synchronized (SingleDemo.class) { if (s == null) s = new SingleDemo();// 如果不将new的对象赋给S,这样就编程了2个对象 } return s;// 返回类类型对象 } }
总结:双重判断的原理://判断如果为空执行下一步,进入同步后,只能进一个线程,其他的线程,在外面等候,当第一个线程创建了一个后,第二个进去的线程再进去判断的后,就有值,这样就不会出现安全问题了
七、线程间的通讯
是指多个线程之间需要协调工作,通讯方法被调用的前提是:同步环境!
线程的思路:
A1说:--去玩会吧--
B说:--我去拿篮球--
A1说:--Go!Go!Go!--
实例:
public class ThreadDemo7 { /** * @param args * 演示线程间的通讯 需求: 1号线程进入同步块,然后被wait() * 2号线程进入同步块,休眠10秒,然后调用notify(),唤醒1号 1号线程被唤醒,说声GO!GO!GO! */ public static void main(String[] args) { A1 a1 = new A1(); B b = new B(); a1.start(); b.start(); } } /* * 静态同步块:静态同步方法互斥,在类中有一个静态同步方法,其他的都必须等它执行完毕后,才能执行 */ class A1 extends Thread { //重写run方法 public void run() { //使线程同步,共享数据是Object类文件 synchronized (Object.class) { try { System.out.println("A1说:--去玩会吧--"); //让该线程进入阻塞状态,并释放CPU执行权,没人叫它,它下面的代码永远不会执行 Object.class.wait(); System.out.println("A1说:--Go!Go!Go!--"); } catch (InterruptedException e) { e.printStackTrace(); } } } } class B extends Thread { public void run() { synchronized (Object.class) { System.out.println("B说:--我去拿篮球--"); //唤醒线程池中的沉睡线程 Object.class.notify(); try { //让该线程进入阻塞状态,2000毫秒,并释放CPU执行权,2000毫秒后,执行权可以回来执行 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
总结:线程间通讯小结
使用wait()、notify()、notifyAll()方法可以完成线程间的通讯,可叫它们通讯方法;
只能在同步环境下调用通讯方法;
只能使用共享数据调用通讯方法;
每个共享数据都有一个线程监狱:执行a.wait()的线程会被关押到a对象的线程监狱中;
若想释放出a对象的线程监狱中的线程,那么需要调用a.notify()文法,该方法只能保证在a对象的线程监狱中释放出一个线程,但不能保证释放的是哪一个;
还可以使用a.notifyAll()方法释放出a对象的监狱中关押的所有线程。
被wait()了的线程不能自己恢复到就绪状态,只能等待其他线程调用同一监视器对象上的notify()或notifyAll()方法来唤醒。
被wait()了的线程会释放共享数据的数据锁,这样其他线程就可以进入他占用的同步环境。
被唤醒的线程恢复到了就绪状态,当再次获取监听器对象的锁后会在wait()处向下运行。
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------