Java线程和IO总结
//接着来创建一个线程类对象
public static void main(String[] args){ Thread t1 = new ThreadA(); //创建一个 t1 线程
Runnable r = new MyRunnable(); //创建一个 Runnable 的实例 r Thread t2 = new Thread(r); //使用一个 Runnable 的实例来创建一个线程 t2
进程的调度: 进程的调度是由 OS 负责的(有的系统为独占式,有的系统为共享式,根据重要性,进
程有优先级)。由 OS 将时间分为若干个时间片,调度系统把每个时间片分给多个进程,而 线程也是类似的。JAVA 在语言级支持多线程。负责分配时间的仍然是 OS。性能调优是根 据时间片划分,时间片会影响整个操作系统。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。 注意 这三个方法都是 ng.Ojbect 的方法! setDeamon(true) 设置线程为后台运行
6.JDK5.0 多线程
新的并发包 java.util.concurrent 实现了线程与业务的分离! 让线程资源不断地重用。
线程的同步 用法一:
synchronized(o){ 原子操作代码块
(同步代码块)
}
哪个线程能拿到 o 的锁标记,哪个线程才能进入这个同步代码块, 等到出了代码块后才释放锁标记。 未加同步可能造成数据不一致和数据不完整的缺陷。
用法二:
public synchronized void method(){ …}
为了让某些工作并行,在 JVM 进程的主线程中开辟多个线程,线程并发执行是一个宏 观概念,微观上是串行的。这就是线程的机制。
2、启动线程的两种方式:
创建线程步骤:首先调用构造方法构造对象,在构造方法中调用 start()方法来配置线程 , 然后由线程执行机制调用 run()。如果不调用 start(),线程将永远不会启动。
synchronized(o1){
//同步代码块是可以嵌套的
synchronized(o2){
}
}
3
lujy@
当一个线程阻塞在 A 对象的锁池里的时候,不会释放其所拥有的其他对象的锁标记。
线程的优先级: 值越大优先级越高,优先级越高被 OS 选中的可能性就越大。(不建议使用,因为不同
t1.start(); t2.start();
}
}
3、线程的生命周期:(线程七状态图)☆
1、数据输入结束;
2、sleep 时间到;
3、
t1
t2 进入可运行状态
t2
1、等待数据输入 2、sleep() 3、t2 调用 t1.join
t2 阻塞
1、 2、异常中止
(得到了锁)
1、 2、调用 yield()
lujy@
接口: Excutor 执行器 Excutors 工具类
ExcutorService
一次执行完后,线程执行不会销毁, 除非 es.shutdown();
用线程池可以控制并发的数量(看池中参数为几) ExcutorService es = ExcutorService.newFixedThreadPool(2); //生成一个固定大小的线程池 Runnable r1 = new Code1(); Runnable r2 = new Code2(); Runnable r3 = new Code3();
… //线程在执行时运行的代码
}
}
public class TestThread{ pulbic static void main(String[] args){ //只有等到所有的线程全部结束之后,主线程才退出。
Thread t1=new ThreadA();
t1.start();
//一个线程只可启动一次,否则抛 IllegalThreadStateException
解决方式 —— 线程间通信:
线程 t1: o.wait()
//让当前运行的线程等待。
前提:必须在对 o 加锁的同步代码块里
1. t1 会释放其所拥有的所有锁标记
2. t1 会进入 o 的等待队列
线程 t2: o.notify() / o.notifyAll() 前提:必须在对 o 加锁的同步代码块里;
操作系统的优先级并不相同,使得程序不具备跨平台性,这种优先级只是粗略地划分)。 设置线程优先级:setPriority(Thread. MAX_PRIORITY); 注:程序的跨平台性:除了能够运行,还必须保证运行的结果。
5、死锁
死锁:多线程中每个线程不释放自己拥有的资源,却申请别的线程的资源,造成死锁。
this.notify()或 this2.notifyAll () 进入 this 对象的锁池,等待 this 对象的锁
this.wait()让当前正在执行 的线程到 this 对象的等待 池中等待;并且同时释放 this 对象的锁。必须出现在 synchronized 代码块内,且 对象必须与 synchronized 加 锁的对象相同。
4
lujy@
但它并不释放对象锁。也就是如果有 synchronized 同步块,其他线程仍然不能访问共享数据 。 注意该方法要捕获异常。
比如有两个线程同时执行(没有 synchronized),一个线程优先级为 MAX_PRIORITY,另 一个为 MIN_PRIORITY,如果没有 sleep()方法,只能高优先级的线程执行完成后,低优先 级的线程才能执行;但如果当高优先级的线程 sleep(5000)后,低优先级就有机会执行了。 join()
③ 线程数据
指的是堆空间共享,而栈空间独立
进程 代码 CPU
数据
线程
CPU 数 CPU
据
代码
代码
Thread1
Thread2
① CPU (何时做) ② 代码 (做什么) ③ 数据 (对谁做)
进程的数据空间独立;线程的数据空间共享, 能充分使用 CPU 的资源。 线程间通信更容易。共享数据就要加锁、解锁,会降低效率。
对于线程的启动有两种方式:
1
lujy@
(1)、继承 Thread,覆盖 run()方法,调用 start()启动; 如: class ThreadA extends Thread{
public void run(){
//run 方法必须为 public void
☆ 小技巧:必要时可利用 Object 中的 互斥锁标记(monitor)、锁池(lock pool)、等待池(wait pool)。 用其 wai程间通讯。
以下附带一些主要方法说明: sleep()
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,
当前运行的线程可以调用另一个线程的 join()方法,当前运行的线程将转入阻塞状态, 直到另一个线程运行完毕,它才进入可运行状态。注意该方法也要捕获异常。
yield() 当一个线程对象调用 yield()方法时会马上交出执行权,回到可运行状态,等待 OS 的再
次调用。 wait()和 notify()、notifyAll()
t2 会从 o 的等待队列中释放一个线程/所有线程。
推荐:尽量用 notifyAll 取代 notify, 因为若用 notify 是由 OS 决定释放哪一个线程。
一些规律: a. 只有运行状态的线程才有机会执行代码! b. 只有等到所有线程终止,进程才结束! c. 当一个对象分配给某线程锁标记时,其它线程不能访问同步方法,但能访问非同步方法! d. 每一个对象都有一个互斥锁标记(monitor),这个锁标记是准备分配给线程的,且只能分
补充:通过 synchronized,可知 Vector 与 ArrayList 的区别就是 Vector 所有的方法都加有 synchronized。所以 Vector 更为安全。但效率也低,考虑并发时也不考虑用 Vector。 同样:Hashtable 比较 HashMap 也是如此。 另外,一个线程可以同时拥有多个对象的锁标记
池化的思想
CPU 代码 数据
拿出 加入
线程池: 一次性创建多个线程,让 其空转。若有多余的加 入,则要先等待着。
eg.字符串池 数据库连接池 线程池 EJB 的 Bean 池 让线程重用; 让程序员从线程控制中解脱出来(专注于线程要干些什么)
自此,Thread 类不用了
5
public void method(){
等价于对当前对象加锁:
synchronized(this){...}
} //在整个方法中,对 this 加锁。
加锁的原则: ① synchronized 所包括的代码要尽可能少,也就是将某些不可分割的操作设置为原子操作。 ② 必要时把 this 换成 static Object 成员,可以保证是对同一个对象加锁。 ③ 要避免线程的死锁。 加锁时注意: 构造方法 不能加锁,因为还没有当前对象; 抽象方法 也不能加锁,因为子类覆盖方法后可以不加 synchronized,父类就有多余的话 。 静态方法可以加锁,而这时是对其类对象加锁。 每一个对象都有一个锁池(lock pool),装的是等待该对象锁标记的线程。
lujy@
4、实际开发中的关注点 ☆
创建几个线程操作比较简单,而在实际开发中,我们真正要关心的是被线程共享的数据, 主要看 synchronized 加在何处。往往不加在线程里面,而是在共享的对象上。 多线程出现故障的原因:
A. 两个线程同时访问一个数据资源(该资源称为临界资源),造成数据不一致和不完整 。 B. 数据的不一致往往是因为一个线程中的两个关联的操作只完成了一步。 避免以上的问题可采用对数据进行加锁的方法: