1. 进程与线程
一个进程拥有多个线程,多个线程共享进程的内存块。操作系统不分配新的内存,因此线程之间通信很容易。不同的进程因处于不同的内存块,因此进程之间通信较为困难。
进程:每个进程都有独立的代码和数据空间(进程上下文) ,进程切换的开销大。
线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
多进程:在操作系统中,能同时运行多个任务程序。
多线程:在同一应用程序中,有多个顺序流同时执行。
2.线程的执行
多线程并非多线程一起执行,而是以很快的速度切换执行,并且切换无序,执行时间也不定。在宏观上给人以同步的感受,但是微观上还是异步执行
Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,
缺省是5(NORM_PRIORITY)。 java中线程是抢占机制,顾名思义,谁抢到谁用!优先级高的线程有更大的机会获得 CPU 时间,优先级低的线程也不是没有机会,
只是机会要小一些罢了
3.临界资源
因为线程的执行时随机的,不受控制的,所以在多线程编程中会有线程安全问题(临界资源,此资源会被多个进程同时访问):
如,int n ; 线程A修改 变量 n 输出,但是在还未输出时,线程B修改了n的值,这就导致A的输出结果不正确。
在银行或者售票系统中,这是完全不能接受的。在java中,采用关键字 synchronized (修饰符),使线程像营业厅的顾客一样,一个排队进行。其所修饰的方法为同步方法。
4. synchronized
只要被synchronized修饰,该对象的所有同步方法,一次只能被访问一个。如果同时被static修饰,那么该类的所有对象一次只能同时被一个线程访问。
/***************************************理解可能偏,看其他文章****************************************************************
访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。
如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。
如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行
synchronized块,写法:
synchronized(object)
{
}
表示线程在执行的时候会对object对象上锁。
synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的代码是可以被多个线程同时访问到的。
******************* ********************************************************************************+/
5.线程的生命周期
创建状态: Thread th = new ThreadClass();
当一个线程处于创建状态时,它仅仅是一个空的线程对象,系统不为他分配哦资源。
就绪状态: th.start();
系统为这个线程分配所需资源,次线程处于课运行状态,但不是运行状态。
运行状态:
该线程获取了cpu的调度
不可运行状态:
调用sleep()方法,注意调用该方法不会打开对象锁,就是说不允许其他线程进入。
调用wait()方法,注意该方法相当于离开了位子,运行其他线程进入。如果该线程要再次调用,需要在队列等待。
调用suspend()方法。
输入输出流发生线程阻塞。
处于这一状态,即使处理器空闲也不会执行该线程。
死亡状态:
自然撤销,线程运行完毕。
stop方法,不推荐使用该方法(貌似被废除了)。
6.线程的使用
java提供两种线程,一种继成,一种接口
1.继承 Thread 类,覆盖方法 run()
2. 实现 Runnable 接口
我也一直想弄了解这两个的区别,不可能同一个东西要用两个方法。
在使用中,应该使用那种方式呢?当然使用哪种方式都行,然而在Runnable接口API文档中已经详细地说明该如何选择这两种方式:大多数情况下,如果只想重写 run() 方法,而不重写其他 Thread 方法,那么应使用 Runnable 接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类(Thread)创建子类。
要多写代码才能理解更多。