Java学习记录6——多线程技术

多线程技术

  “程序(Program)”是一个静态的概念,一般对应于操作系统中的一个可执行文件
  执行中的程序叫做进程(Process),是一个动态的概念
进程具有如下特点:

  1. 进程是程序的一次动态执行过程, 占用特定的地址空间。
  2. 每个进程由3部分组成:cpu、data、code。每个进程都是独立的,保有自己的cpu时间,代码和数据,即便用同一份程序产生好几个进程,它们之间还是拥有自己的这3样东西,这样的缺点是:浪费内存,cpu的负担较重。
  3. 多任务(Multitasking)操作系统将CPU时间动态地划分给每个进程,操作系统同时执行多个进程,每个进程独立运行。以进程的观点来看,它会以为自己独占CPU的使用权。

   一个进程可以产生多个线程。同多个进程可以共享操作系统的某些资源一样,同一进程的多个线程也可以共享此进程的某些资源(比如:代码、数据),所以线程又被称为轻量级进程(lightweight process)。

	1. 一个进程内部的一个执行单元,它是程序中的一个单一的顺序控制流程。
	2. 一个进程可拥有多个并行的(concurrent)线程。
	3. 一个进程中的多个线程共享相同的内存单元/内存地址空间,可以访问相同的变量和对象,而且它们从同一堆中分配对象并进行通信、数据交换和同步操作。
	4. 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快。
	5. 线程的启动、中断、消亡,消耗的资源非常少。

Java学习记录6——多线程技术_第1张图片
线程和进程的区别
  线程和进程最根本的区别在于:进程是资源分配的单位,线程是调度和执行的单位。

继承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接口的方式要通用一些。

线程状态Java学习记录6——多线程技术_第2张图片
终止线程的典型方式

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

  1. sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。使用sleep,无论cpu是否空闲,只有休眠时间到了才能继续 执行。
  2. yield()方法:可以让正在运行的线程直接进入就绪状态,让出CPU的使用权。使用yield,一旦cpu空闲,线程即可继续运行。

线程的联合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操作

死锁及解决方案
  多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。
(死锁结合具体的资源,不作详细说明)

线程并发协作(生产者/消费者模式)
Java学习记录6——多线程技术_第3张图片

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实现。

你可能感兴趣的:(Java学习记录6——多线程技术)