java能够支持多线程,多线程是实现并发机制的一种有效手段,和进程一样,线程也是实现并发的一个基本单位,但是线程是比进程更小的单位,线程是在进程的基础上的进一步划分。所谓所线程就是说一个进程在运行的过程中可以产生多个线程,这些线程可以同时存在,同时运行,一个进程可能包含有多个同时执行的线程。
首先,线程的实现:直接继承Thread类和实现Runnable接口。这两种方式都能够实现多线程先给出例子:
继承Thread类:
class ThreadT extends Thread{
public ThreadT(String name) {
super(name);
}
@Override
public void run() {
for(int i=0;i<20;i++)
System.out.println(this.getName()+"running"+i);
}
}
public class ThreadTest {
public static void main(String[] args) {
new ThreadT("Thread线程一").start();
new ThreadT("Thread线程二").start();
}
}
/**
* 结果:
*
* Thread线程一running0
Thread线程一running1
Thread线程一running2
Thread线程二running0
Thread线程一running3
Thread线程二running1
Thread线程一running4
Thread线程二running2
Thread线程一running5
Thread线程二running3
Thread线程二running4
Thread线程二running5
Thread线程二running6
Thread线程二running7
*/
启动线程不能直接调用run()方法,必须调用start()方法,因为线程的执行需要cpu的调度,同时每个线程都只能调用一次start()方法
实现Runnable接口的多线程:两个线程一个打印“-”一个打印“|”同时打印:
public class ThreadTest01 {
public static void main(String[] args) {
// new Thread(new PrintHeng()).start();
// new Thread(new PrintShu()).start();
RunnableTest runnable1=new RunnableTest();
new Thread(runnable1,"thread1").start();
new Thread(runnable1,"thread2").start();
new Thread(runnable1,"thread3").start();
new Thread(runnable1,"thread4").start();
}
}
class PrintHeng implements Runnable{ // 打印横线的线程
public void run() {
for(int i=0;i<20;i++)
{
System.out.print("-");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class PrintShu implements Runnable{ // 打印竖线的线程
public void run() {
for(int i=0;i<20;i++)
{
System.out.println("|");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行能同时打印横线和竖线,但是注意到
线程的start方法调用还是通过Thread类来实现的,这一点下面会给出解释
既然通过Thread类和Runnable接口都可以实现多线程,那么这两种方法有什么区别呢,首先给出Thread类的定义:
public class Thread implements Runnable {
//... 中间很多变量省略
//...
/* What will be run. */
private Runnable target; // 将目标线程作为自己的参数
//... 中间部分省略
//...
public void run() {
if (target != null) { // 实际上就是调用了目标线程的run方法
target.run();
}
}
}
从定义中可以看出,Thread类也实现了Runnable 的接口,如果我们自己实现的线程也是实现Runnable接口方式的话就有了下面的关系:
可见当我们Thread类就相当于是我们目标线程(MyThread)的一个代理,通过target的start()方法来调用MyThread中的run方法,而不是直接运行我们Mythread中的run(),因为直接调用run()的话并不需要cpu的调度相当于执行了一个普通方法。
除此之外两种方式在共享变量时也是有所区别的,使用一个简单的例子:
package edu.hue.jk.thread;
class MyThread extends Thread{
private int i=0;
public void run(){
System.out.println("Thread继承方式:"+"----"+i++);
}
public MyThread() {
super();
}
}
public class ThreadTest02 {
public static void main(String[] args) {
for(int i=0;i<10;i++)
new Thread(new MyThread()).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyRunnable runnable=new MyRunnable();
for(int i=0;i<10;i++)
new Thread(runnable).start(); // 构造同一实例的多个线程
}
}
class MyRunnable implements Runnable{
private int i=0;
public void run() {
System.out.println("Runnable实现方式:"+"----"+i++);
}
public MyRunnable() {
super();
}
}
实验结果:
Thread继承方式:----0
Thread继承方式:----0
Thread继承方式:----0
Thread继承方式:----0
Thread继承方式:----0
Thread继承方式:----0
Thread继承方式:----0
Thread继承方式:----0
Thread继承方式:----0
Thread继承方式:----0
Runnable实现方式:----0
Runnable实现方式:----1
Runnable实现方式:----2
Runnable实现方式:----3
Runnable实现方式:----4
Runnable实现方式:----5
Runnable实现方式:----6
Runnable实现方式:----7
Runnable实现方式:----8
Runnable实现方式:----9
说明使用Runnable方式实现多线程时候,可以实现多个线程共享同一个实例(资源共享的效果)。
三个静态方法用来控制进程的优先级:
除此之外还可以使用object中的三个方法来对进程进行调度:
this.notify(); // 唤醒另外一个进程
this.notifyAll(); // 唤醒所有进程
this.wait(); // 等待
this.wait(timeout); // 等待多长时间
使用进程同步:用一个例子来说明同步的重要性
package edu.hue.jk.thread;
//要理解进程同步首先要搞清楚进程同步的定义:
/**
* 在操作系统中进程同步是指,系统中多个进程中发生的事件存在某种时序关系,需要相互协作,共同完成任务。具体的说一个线程运行在某一点时
* 需要另外一个线程为它提供消息,在未获得消息之前一直处在等待状态,获得消息之后该进程被唤醒处于就绪状态。而且当多个线程访问同一份资源(贡献资源)
* 的时候,可能会引起冲突,所以要加入同步机制来控制多个线程之间的访问顺序,一遍造成读写不一致的情况。
* 现在假设一个网上售票系统,有三个点可以售票,假设总共有五张票,程序如下:
* @author dell
*
*/
public class WhySynchronized {
/**
* @param args
*/
public static void main(String[] args) {
TiketThread tiket=new TiketThread();
new Thread(tiket,"tiket1").start();
new Thread(tiket,"tiket2").start();
new Thread(tiket,"tiket3").start();
}
}
class TiketThread implements Runnable{ // 充分说明了进程同步的重要性
static int tiket=5;
public void run() {
//synchronized(this){ // 不使用同步
while(true){
if(tiket>0)
{
try {
Thread.sleep(200); // 加入延时方便查看期待的结果
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"selling tikets"+tiket);
tiket--;
}
else
break;
}
//}
}
}
可见没有使用同步,使得多线程的使用得不到我们需要的效果,将使用同步的注释去掉就可以得到正确的结果。
为了跟进一步的理解进程的同步使用方法,以及如何去操作进程,引入生产者和消费者的两个例子:
<span style="color:#000000;">package edu.hue.jk.thread;
/**
* 用来描述生产者消费者问题 生产者不断的生产产品,消费者不断的取出产品;生产者生产一个产品,消费者就要取出一个产品,生产者不能连续生产,消费者也不能连续取产品
* 对于生产者和消费者来说,产品就是共享资源,而且不能同时去控制产品,所以使用同步操作
* 使用信号量来控制生产者和消费者的协调工作:
* s1=1 表示生产者可以生产,s1=0表示不能生产 初始值为1
* s1=0表示消费者可以取产品 s1=1 表示不能取
**/
public class ProduceAndConsumer {
public static void main(String[] args) {
Product pro=new Product();
pro.setS1(1);
new Thread(new Produce(pro)).start();
new Thread(new Consumer(pro)).start(); // 这里要怎样修改
}
}
class Product{
private String productName;
int s1=1; // 用来协调生产者和消费者的信号量
public int getS1() {
return s1;
}
public void setS1(int s1) {
this.s1 = s1;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
}
class Produce implements Runnable{
Product product;
public Produce(Product product) {
super();
this.product = product;
}
public void run() {
while(true){ // 生产者不断的生产产品
synchronized(product){ //使用同步块
if(product.getS1()==1) // 能够生产
{
product.setProductName("productName");
System.out.println("生产产品:"+product.getProductName());
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
product.setS1(0); // 生产者生产之后消费者能够取出产品
product.notify(); // 唤醒消费者
}
else // 不能够生产
{
System.out.println("不能生产产品:");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
product.wait(); // 等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
class Consumer implements Runnable{
Product product;
public Consumer(Product product) {
super();
this.product = product;
}
public void run() {
while(true){
synchronized(product){
if(product.getS1()==0)
{
System.out.println("消费者取出产品:"+product.getProductName());
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
product.setS1(1);
product.notify(); // 唤醒生产者
}
else
{
System.out.println("不能取出产品:");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
product.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}</span>
得到的结果
生产产品:productName
不能生产产品:
消费者取出产品:productName
不能取出产品:
生产产品:productName
不能生产产品:
消费者取出产品:productName
不能取出产品:
生产产品:productName
不能生产产品:
消费者取出产品:productName
不能取出产品:
生产产品:productName
生产者消费者问题变形:
<span style="color:#000000;">package edu.hue.jk.thread;
import java.util.LinkedList;
import java.util.List;
/**
*<span style="color:#cc0000;"> 生产者和消费者问题变形: 现在有一个容量固定的缓冲区,缓冲区满时生产者停止生产,缓冲区空时消费者停止消费
* 前边使用的是同步块,下面使用同步方法来实现
* @author dell
</span> *
*/
public class ProduceAndConsumer02 {
public static void main(String[] args) {
ProductList list=new ProductList(5);
new Thread(new Produce02(list)).start();
new Thread(new Consumer02(list)).start();
}
}
class Product02 {
String productName;
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Product02(String productName) {
super();
this.productName = productName;
}
}
// 表示缓冲区
class ProductList{
int length ;// 表示缓冲区的容量
int i=1; // 表示铲平的序号
List<Product02> productList;
public ProductList(int length) {
super();
this.length = length;
productList=new LinkedList<Product02>();
}
public ProductList() {
super();
productList=new LinkedList<Product02>();
}
// 使用同步方法生产产品
public synchronized void ProduceAProduct(){
while(true){
try {
Thread.sleep(300);
} catch (InterruptedException e1) { //为了观察效果而设置的延迟操作
e1.printStackTrace();
} //
if(length>productList.size()){ // 可以继续生产产品
Product02 pro=new Product02("product"+i);
productList.add(pro); // 加入缓冲区
System.out.println("生产产品:"+pro.getProductName());
i++;
notify();
}
else{
try {
System.out.println("生产者等待。。。。。。");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public synchronized void getAProduct(){
while(true){
try {
Thread.sleep(300);
} catch (InterruptedException e1) {
e1.printStackTrace();
} //
if(productList.size()>0){ // 可以继续生产产品
Product02 pro=productList.get(0); // 始终取第一个产品
productList.remove(0); // 从缓冲区取出
System.out.println("消费者取出:"+pro.getProductName());
notify();
}
else{
try {
System.out.println("消费者等待。。。。。。");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Produce02 implements Runnable{
ProductList list;
public void run() {
list.ProduceAProduct();
}
public Produce02(ProductList list) {
super();
this.list = list;
}
}
class Consumer02 implements Runnable{
ProductList list;
public void run() {
list.getAProduct();
}
public Consumer02(ProductList list) {
super();
this.list = list;
}
}</span>
运行结果:
生产产品:product1
生产产品:product2
生产产品:product3
生产产品:product4
生产产品:product5
生产者等待。。。。。。
消费者取出:product1
消费者取出:product2
消费者取出:product3
消费者取出:product4
消费者取出:product5
消费者等待。。。。。。
生产产品:product6
生产产品:product7
生产产品:product8
生产产品:product9
生产产品:product10
生产者等待。。。。。。
消费者取出:product6