多线程技术
“程序(Program)”是一个静态的概念,一般对应于操作系统中的一个可执行文件
执行中的程序叫做进程(Process),是一个动态的概念。
进程具有如下特点:
1. 进程是程序的一次动态执行过程, 占用特定的地址空间。
2. 每个进程由3部分组成:cpu、data、code。每个进程都是独立的,保有自己的cpu时间,代码和数据,即便用同一份程序产生好几个进程,它们之间还是拥有自己的这3样东西,这样的缺点是:浪费内存,cpu的负担较重。
3. 多任务(Multitasking)操作系统将CPU时间动态地划分给每个进程,操作系统同时执行多个进程,每个进程独立运行。以进程的观点来看,它会以为自己独占CPU的使用权。
一个进程可以产生多个线程。同多个进程可以共享操作系统的某些资源一样,同一进程的多个线程也可以共享此进程的某些资源(比如:代码、数据),所以线程又被称为轻量级进程(lightweight process)。
1. 一个进程内部的一个执行单元,它是程序中的一个单一的顺序控制流程。
2. 一个进程可拥有多个并行的(concurrent)线程。
3. 一个进程中的多个线程共享相同的内存单元/内存地址空间,可以访问相同的变量和对象,而且它们从同一堆中分配对象并进行通信、数据交换和同步操作。
4. 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快。
5. 线程的启动、中断、消亡,消耗的资源非常少。
线程和进程的区别
线程和进程最根本的区别在于:进程是资源分配的单位,线程是调度和执行的单位。
继承Thread类实现多线程
继承Thread类实现多线程的步骤:
1. 在Java中负责实现线程功能的类是java.lang.Thread 类。
2. 可以通过创建 Thread的实例来创建新的线程。
3. 每个线程都是通过某个特定的Thread对象所对应的方法run( )来完成其操作的,方法run( )称为线程体。
4. 通过调用Thread类的start()方法来启动一个线程。(特别注意这里只调用线程里的run方法)
public class TestThread extends Thread {//自定义类继承Thread类
//run()方法里是线程体
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.getName() + ":" + i);//getName()方法是返回线程名称
}
}
public static void main(String[] args) {
TestThread thread1 = new TestThread();//创建线程对象
thread1.start();//启动线程
TestThread thread2 = new TestThread();
thread2.start();
}
}
此种方式的缺点:如果我们的类已经继承了一个类(如小程序必须继承自 Applet 类),则无法再继承 Thread 类。
Runnable接口实现多线程
实现Runnable接口的同时还可以继承某个类。所以实现Runnable接口的方式要通用一些。
public static void main(String[] args) {
TestThreadCiycle ttc = new TestThreadCiycle("线程A:");
Thread t1 = new Thread(ttc);// 新生状态
t1.start();// 就绪状态
for (int i = 0; i < 100; i++) {
System.out.println("主线程" + i);
}
ttc.terminate();
System.out.println("ttc stop!");
}
暂停线程执行sleep/yield
线程的联合join()
线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。
public void run() {
System.out.println("A线程");
Thread B = new Thread();
try {
B.join();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
System.out.println("出现问题);
System.exit(1);
}
System.out.println("A线程结束");
}
注意,如果不加B.join,那么A,B两个线程是并行的,join的作用是B线程执行完了,才能继续执行A线程剩下的。
线程优先级的设定
Thread t1 = new Thread(new Mythread(), "t1");
Thread t2 = new Thread(new Mythread(), "t2");
t1.setPriority(1);
t2.setPriority(10);
此外,可以通过Thread.currentThread()获取正在运行的线程对象
线程同步
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。 这时候,我们就需要用到“线程同步”。
synchronized (object){}
这里的含义是针对这个object已经上了锁,理解同P-V操作
死锁及解决方案
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。
(死锁结合具体的资源,不作详细说明)
package xyzc.multithreading;
import java.util.ArrayList;
/**
* 测试线程并发协作
* 以生产者/消费者为例
* @author 心悦则呈
*
*/
public class TestThreadConcurrentCooperation {
public static void main(String[] args) {
Buffer buffer = new Buffer();
Thread scThread = new Thread(new ShengChan(buffer));
Thread xfThread = new Thread(new XiaoFei(buffer));
scThread.start();
xfThread.start();
}
}
class Mantou{
int id;
public Mantou(int id) {
super();
this.id = id;
}
}
class Buffer{
int index=0;
final int length = 5;
ArrayList<Mantou> montouList = new ArrayList<Mantou>();
public synchronized void push(Mantou m) {
if(montouList.size()==length) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.notify();
montouList.add(m);
}
public synchronized Mantou pop() {
while(montouList.size()==0) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.notify();
Mantou tempMantou = montouList.get(montouList.size()-1);
montouList.remove(montouList.size()-1);
return tempMantou;
}
}
class ShengChan implements Runnable {
Buffer buf = null;
public ShengChan(Buffer buf) {
super();
this.buf = buf;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println("生产馒头:" +i);
Mantou scm = new Mantou(i);
buf.push(scm);
}
}
}
class XiaoFei implements Runnable{
Buffer buf = null;
public XiaoFei(Buffer buf) {
super();
this.buf = buf;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
Mantou xfm = buf.pop();
System.out.println("消费馒头:"+xfm.id);
}
}
}
任务定时调度
通过Timer和Timetask,我们可以实现定时启动某个线程。
public class TestTime {
public static void main(String[] args) {
Timer t1 = new Timer();
Mytask mt1 = new Mytask("xyzc1");
Mytask mt2 = new Mytask("xyzc2");
t1.schedule(mt1, 1000);
t1.schedule(mt2, 1000);
}
}
class Mytask extends TimerTask{
String name;
public Mytask(String name) {
super();
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.println(this.name+":"+i);
}
}
}
在实际使用时,一个Timer可以启动任意多个TimerTask实现的线程,但是多个线程之间会存在阻塞。所以如果多个线程之间需要完全独立的话,最好还是一个Timer启动一个TimerTask实现。