java之多线程
在前面的操作系统的进程和线程中,对线程已经做了一些详细的讲解,对于java中多线程实现,本文将再做一些介绍。
线程的状态
对于java中线程状态,我们可以用五状态模型来表示,分别为:
创建,就绪,运行,阻塞,终止五态
创建状态:JVM调用main()方法会创建一个主线程,主线程通过调用线程对象的start()方法启动一个子线程,新建的线程处于创建状态,当线程运行的各种条件具备后,进入就绪队列。
其他的各种状态参考进程与线程中的讲解。
线程的实现(Tread类和Runnable接口)
java中创建一个线程有两种方法,分别是通过继承Thread类和实现Runnable接口。具体介绍如下:
Java中可通过Thread类及其子类和Runnable接口实现多线程。
Thread类可以直接定义线程对象,但一般需要定义Thread类的子类来实现多线程,以满足程序设计的特殊要求。受到java单继承的限制,在实际应用中,几乎所有的多线程应用都采用实现runnable接口的方式实现多线程。
也就是说,如果新建的类要继承其他类,因为java中不支持多继承,因此只能通过实现java.lang.Runnable 接口完成多线程任务,runnable适合多个相同程序代码的线程去处理同一资源的情况,把虚拟cpu(线程)同程序的代码、数据有效分离。
两种线程的实现方式的代码如下:
public class ThreadDemo extends Thread {
int i = 0;
public void run(){
while (i<10){
System.out.println("实现Thread类继承的线程,正在占有处理机运行……"+i);
try{
sleep(1000);
i++;
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
在main函数当中,我们只需实例化该类,调用start()方法便可以创建一个线程了。
public class RunnableDemo implements Runnable {
int i = 0;
@Override
public void run(){
while (i<10) {
System.out.println("实现Runnable接口的线程,正在占有处理机运行……" + i);
try {
Thread.sleep(1000);
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
main函数中实现方法:
Thread thread = new Thread(RunnableDemo );
Thread.start();
线程的基本控制
线程的启动:thread.start()
线程的结束:设定一个标记变量,以结束相应的循环及方法
暂时阻止线程的执行:Try{Thread.sleep(1000);}catch(InterruptedException e){ }
设定线程优先级:setPriority(int priority)方法
Max_priority 线程可以具有的最高优先级(通常是10)
Min_priority 线程可以具有的最高优先级(通常是1)
Normal_priority 分配给线程的默认优先级(通常是5)
线程的分类
Java中有两类线程:User Thread(用户线程)和Daemon Thread(守护线程),Daemon线程的作用是为其他线程的运行提供服务,如垃圾回收线程,在java程序中,若还有非Daemon线程,整个程序就不会结束。
通过setDaemon(true)方法设定守护线程
线程的调度
Jvm负责给线程分配cpu,称为线程调度,原则:高优先级的线程先运行同等优先级的多个线程直接按时间片轮转分配cpu资源
存在的问题
线程的不确定性:Java中一个简单的语句对应到cpu中的多条指令,当一个线程刚刚执行了一个指令,被调度出后,后续线程重复调用原来的数据,导致线程的不确定性(不是原子性的)
线程的同步
线程同步:同时运行的线程需要共享数据,就必须考虑其它线程的状态和行为,这时就需要实现同步Java引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应一个monitor(监视器),它上面有一个称为“互斥锁(lock,mutex)”的标记,这个标记用来保证在任意时刻,只能有一个线程访问该对象。关键字synchronized用来与对象的互斥锁联系
synchronized的用法:
对代码片段synchronized(对象){}
对某个方法:synchronized放在方法声明中,Public synchronized void push(char c){}相当于synchronized(this),表示整个方法为同步方法
线程同步控制:
使用wait()方法可以释放对象锁
使用notify()或notifyAll()可以让等待的一个或所有线程进入就绪状态
Java里面可以将wait和notify放在synchronized中,在synchronized执行期间,线程调用对象的wait方法,会释放对象锁标识,然后进入等待状态,然后其他线程调用notify()或者notify()方法通知正在等待的线程。
具体代码如下:
class CubbyHole {
private int index = 0;
private int[] data = new int[3];
public synchronized void put(int value){
while (index == data.length){
try{
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
data[index++] = value;
this.notify();
}
public synchronized int get(){
while (index <=0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int value = data[index--];
this.notify();
return value;
}
}
另外的问题
在简单的同步中,加锁和解锁很容易造成死锁问题,也就是相互等待。java在1.5以后引入了一些方法来解决多线程问题。
并发API(java.util.concurrent)
从JDK1.5以后提供了一系类更好用的如单变量、集合、Timer、线程池。
原子变量java.util.concurrent.atomic包AtomicInteger类
GetAndIncrement()方法保证线程访问时是安全的
并发的集合类Java.util.concurrent包中增加一些类CopyOnWriteArrayList、CopyOnWriteSet
适合很少写入而读取频繁的对象
ConcurrentHashMap
ArrayBlockingQueue生产者消费者使用put和get
使用线程池
线程池相关的类ExecutorService接口、ThreadPoolExecutor类Executors工具类
常见的用法ExecutorService pool = Executors.newCachedThreadPool();
使用其execute(Runnable r)方法