线程与并行编程【总结】

1. 程序、进程与线程的关系?

答:程序是一段静态的代码,是应用软件执行的蓝本。 进程是程序一次动态执行过程,它对应从代码加载、执行到执行完毕的一个完成过程。同一段程序可以被对此加载到系统的不同内存区域分别执行,形成不同的进程。 线程是比进程更小的执行单位。一个进程在执行过程中,可以产生多个进程,形成多条执行线索。进程和线程都是动态的,都有本身的产生、存在和消亡的过程。

每个进程都有一段专用的内存区域,并以PCB(进程控制块)作为它存在的标志。而线程间可以共享相同的内存单元(包括代码块和数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作。

2. 创建进程对象的两种方法?

答:

  • 通过继承Thread类:从Thread派生出一个新类,加入属性和方法,同时覆盖run( )方法,使用start( )方法即可启动该线程。例如:
public class TestThread1 {

	public static void main(String[] args) {
		Thread thread=new MyThread();
		thread.start();
	}
}
class MyThread extends Thread{
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println(i);
		}
	}
}
  • 通过向Thread( )构造方法传递Runnable( )对象,例如:
public class TestThread2 {

	public static void main(String[] args) {
		MyTask mytask=new MyTask(13);
		Thread thread=new Thread(mytask);
		thread.start();
	}
}
class MyTask implements Runnable{
	private int n;
	public MyTask(int n) {
		this.n=n;
	}
	public void run() {
		for(int i=0;i<n;i++) {
			System.out.println(i);
			try {
				Thread.sleep(500);
				//表示线程等待(休眠)500毫秒,休眠过程可能被中断,故要捕获异常
			}catch (InterruptedException e) {}
		}
	}
}

 

两种方法的比较:直接使用Thread类,编写简单,但不能再继承其他类;使用Runnable接口,可以将Thread类与要处理的任务的类分开,还可以继承其它类。

3. 多线程的创建?

答:一个程序的多个线程可以同时运行,可以共享代码及数据。如下例子中,共建了7个线程,前6个共享相同的代码,前三个共享对象r1,后三个共享对象r2,最后一个线程无穷循环显示当前的时间。从程序可看出几个线程同时运行的情况。

import java.text.SimpleDateFormat;
import java.util.Date;

public class TestThread3 {
	
	public static void main(String[] args) {
		Runner r1=new Runner(1);
		Thread t1=new Thread(r1);
		Thread t2=new Thread(r1);
		Thread t3=new Thread(r1);
		
		Runner r2=new Runner(10);
		Thread t4=new Thread(r2);
		Thread t5=new Thread(r2);
		Thread t6=new Thread(r2);
		
		Timer timer=new Timer();
		Thread t7=new Thread(timer);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
		t6.start();
		t7.start();
	}
}
class Runner implements Runnable{
	int id;
	Runner(int id) {
		this.id=id;
	}
	public void run() {
		int i=0;
		while(true) {
			i++;
			System.out.println("ID:"+id+" No. "+i);
			try {
				Thread.sleep(500);
			}catch (InterruptedException e) {}
		}
	}
}
class Timer implements Runnable{
	public void run() {
		while(true) {
			System.out.println(new SimpleDateFormat().
					format(new Date()));
			try {
				Thread.sleep(1000);
			}catch (InterruptedException e) {}
		}
	}
}

结果:

ID:1 No. 1
ID:10 No. 1
ID:1 No. 1
ID:1 No. 1
ID:10 No. 1
ID:10 No. 1
18-2-8 下午2:44
ID:1 No. 2
ID:1 No. 2
ID:1 No. 2
ID:10 No. 2
ID:10 No. 2
ID:10 No. 2
ID:1 No. 3
ID:10 No. 3
ID:10 No. 3
ID:1 No. 3
ID:10 No. 3
ID:1 No. 3
18-2-8 下午2:44
ID:1 No. 4
ID:10 No. 4
ID:10 No. 4
ID:10 No. 4
ID:1 No. 4
ID:1 No. 4
ID:1 No. 5
ID:10 No. 5
ID:10 No. 5
ID:10 No. 5
ID:1 No. 5
ID:1 No. 5
18-2-8 下午2:44
...

 

4. Timer类的使用?

答:对于需要每个一定时间重复执行代码的任务,可以用线程来实现,不过也可以用JDk1.5以上版本提供java.util.Timer来实现。 

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

class MyTimerTask extends TimerTask{
	public static void main(String[] args) {
		Timer timer=new Timer("Show n");
		TimerTask task=new MyTimerTask();
		timer.schedule(task, 1000,1000);
		//前一个1000表示第一个执行以前套要等待的时间,后一个1000
		//表示每两次执行的时间间隔,单位为毫秒
	}
	int n=0;
	public void run() {
		n++;
		System.out.print(new Date());
		System.out.println("---"+n);
	}
}

 

5. 对线程的控制?

答:

1)结束线程:给线程设定标记变量来决定终止。

import java.util.Date;

public class ThreadTerminateByFlag {

	public static void main(String[] args) {
		Timer1 timer=new Timer1();
		Thread thread=new Thread(timer);
		thread.setName("Timer");
		thread.start();
		for(int i=0;i<15;i++) {
			System.out.print(i+" ");
			try {
				thread.sleep(500);
			}catch (InterruptedException e) {}
		}
		timer.stopRun();
	}

}
class Timer1 implements Runnable {
	boolean flag=true;
	public void run() {
		while(flag) {
			System.out.println("---"+new Date());
			try {
				Thread.sleep(1000);
			}catch (InterruptedException e) {}
		}
		System.out.println(Thread.currentThread().getName()+" Stop");
	}
	public void stopRun() {
		flag=false;
	}
}


结果:

0 ---Thu Feb 08 16:07:17 CST 2018
1 2 ---Thu Feb 08 16:07:18 CST 2018
3 4 ---Thu Feb 08 16:07:19 CST 2018
5 6 ---Thu Feb 08 16:07:20 CST 2018
7 8 ---Thu Feb 08 16:07:21 CST 2018
9 10 ---Thu Feb 08 16:07:22 CST 2018
11 12 ---Thu Feb 08 16:07:23 CST 2018
13 14 ---Thu Feb 08 16:07:24 CST 2018
Timer Stop

2)暂时阻止线程的执行:

 

  •  sleep( )方法,可以给优先级较低的线程以执行的机会。
  • join( )方法,可以将一个线程加入到本线程中,本线程的执行会等另一线程执行的完毕。
public class ThreadJoin {
	public static void main(String[] args) {
		Runner1 runner=new Runner1();
		Thread thread=new Thread(runner);
		thread.start();
//		try {
//			thread.join();
//		}catch (InterruptedException e) {}
		for(int i=100;i<110;i++) {
			System.out.println("\t"+i);
			try {
				thread.sleep(100);
			}catch (InterruptedException e) {}
		}
	}
}
class Runner1 implements Runnable{
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println(i);
			try {
				Thread.sleep(100);
			}catch (InterruptedException e) {}
		}
	}
}


若没有join( ),则两个线程同时进行,结果如下:

	100
0
	101
1
	102
2
	103
3
	104
4
	105
5
	106
6
	107
7
	108
8
9
	109


若将join( )加上,主程序的线程会等待Runner1的线程执行完毕再进行,结果如下:

0
1
2
3
4
5
6
7
8
9
	100
	101
	102
	103
	104
	105
	106
	107
	108
	109

3)设定线程优先级:需要注意的是,新建线程将继承创建它的父线程的优先级,一边情况下,主线程具有普通优先级,可以通过调用Thread类的方法setPriority( ) 来修改优先级。

public class TestThreadPriority {
	public static void main(String[] args) {
		Thread t1=new Thread(new Runner2("我"));
		Thread t2=new Thread(new Runner2("你"));
		Thread t3=new Thread(new Runner2("他"));
//		t1.setPriority(Thread.MIN_PRIORITY);//最小优先级
//		t2.setPriority(Thread.NORM_PRIORITY);//普通优先级
//		t3.setPriority(Thread.MAX_PRIORITY);//最高优先级
		t1.start();
		t2.start();
		t3.start();
	}
}
class Runner2 implements Runnable{
	String id;
	Runner2(String id){
		this.id=id;
	}
	public void run() {
		for(int i=0;i<50;i++) {
			if(i%10==0) System.out.println();
			Thread.currentThread().yield();
			System.out.print(id);
		}
	}
}

当三个线程具有相同优先级时:

你我他你我他你我他你我他你我他你我他你我他你我他你我他你
我
他
你我他你我他你我他你我他你我他你他我他我他你我你他我你他
我
你
他我你我他你我他你我他你我你他你我你他我你他你我你
他我你他我
你他
我你你他我你他我你他我你他我你他我你他我你
他我你他我你他我
你我他
我他你我他你我他你我他你我他你我他你我他你我他我他他

当线程具有不同优先级时:

他他他他他他他他他他
你他他他他他他他他你他他
他他他他他你他他他他他
他你他他他他他他你他他你他
他他你他他你他你他你
他你他你他他你你你你你
你我你我你
我你我你我你我你你你我你我你我你我
你
你我你我你我你你你你你你你
我你你你我你你我你你你我你你我我我
我我我我我我我我我我
我我我我我我我我我我
我我我我我我我我我我


注意到,相同优先级一般遵循先到先服务的原则,但是不一定十分严格,这取决于Java虚拟机的实际情况。

 

4)设定Daemon线程:线程分为Daemon线程(后台线程、守护线程)和非Daemon线程,在Java程序中,若还有非Daemon线程,程序不会结束;当线程都是守护线程时,Java虚拟机退出。简单地说,主线程会强制结束守护线程。

 

import java.util.Date;

public class TestThreadDaemon {
	public static void main(String[] args) {
		Thread thread=new MyThread3();
		thread.setDaemon(true);
		thread.start();
		
		System.out.println("Main--"+new Date());
		try {
			Thread.sleep(500);
		}catch (InterruptedException e) {}
		System.out.println("Main End");
	}
}
class MyThread3 extends Thread{
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println(i+"--"+new Date());
			try{
				Thread.sleep(100);
			}catch (InterruptedException e) {}
		}
	}
}

主线程只运行500ms,而Daemon线程在显示5行后就被停止了,结果如下:

Main--Thu Feb 08 17:28:13 CST 2018
0--Thu Feb 08 17:28:13 CST 2018
1--Thu Feb 08 17:28:13 CST 2018
2--Thu Feb 08 17:28:13 CST 2018
3--Thu Feb 08 17:28:13 CST 2018
4--Thu Feb 08 17:28:13 CST 2018
Main End

注意,垃圾回收线程是后台线程,且其优先级很低。

 

6. synchronized关键字?

答:前面的线程都是独立的,异步执行的,即每个线程都包含了运行时所需要的数据或方法,但是经常要有一些同时运行的线程需要共享数据,例如一个线程向文件写数据,同时另一个线程从同一文件中读取数据等。下面例子两线程共用同一资源,Counter线程操作对象num,线程中调用increase()方法,使得x、y同时增加,在main()中调用testEquals()方法以检查x、y是否相等:

public class SyncCounter1 {
	public static void main(String[] args) {
		Num num=new Num();
		Thread counter1=new Counter(num);
		Thread counter2=new Counter(num);
		for(int i=0;i<10;i++) {
			num.testEquals();
			try {
				Thread.sleep(100);
			}catch (InterruptedException e) {}
		}
	}
}
class Num{
	private int x=0;
	private int y=0;
	synchronized void increase() {
		x++; y++;
	}
	synchronized void testEquals() {
		System.out.println(x+","+y+":"+(x==y));
	}
}
class Counter extends Thread{
	private Num num;
	Counter(Num num){
		this.num=num;
		this.start();
	}
	public void run() {
		while(true) {
			num.increase();
		}
	}
}

increase( )方法和testEquals( )方法使用关键字synchronized修饰时,结果如下:

0,0:true
2791947,2791947:true
5157509,5157509:true
6209361,6209361:true
7640346,7640346:true
9442662,9442662:true
11938149,11938149:true
14724821,14724821:true
16210786,16210786:true
18819344,18819344:true

不使用synchronized时,

0,0:true
2219421,2223885:false
32105246,32115098:false
74136253,74146104:false
121855709,121889105:false
152650141,152660034:false
188824337,188838074:false
210344800,210359524:false
241558281,241596777:false
294029878,294044628:false

从结果中可看到,大部分时间内x、y并不相等,问题是,当一个线程执行了x++而未执行y++时,系统调度到另一个线程执行 x++及y++,就会出现x、y不相等的情况。 而加入关键字synchronized,防止了两个线程访问相同资源时的冲突,调用任何synchronized方法时,对象就会被锁定,不可再调用那个对象的其它任何synchronized方法,除非第一个方法完成了自己的工作,并解除锁定。

 

7. 线程间的同步控制?

答:以“生产者-消费者问题”这个一般性模型来谈论线程间的同步控制问题,系统中使用某类资源的线程称为消费者,产生或释放的同类资源的线程称为生产者。例如,生产者产生从0~9的整数,将它们储存在名为“CubbyHole ”的对象中并打印,然后调用sleep( )方法使生产者线程在一个随机产生的0~100秒的时间段内休息,同时消费者通过get( )方法取得数据并输出。

class CubbyHole{
	private int seq;
//	private boolean available=false;
	
	public synchronized int get() {
//		while(available==false) {
//			try {
//				wait();//waits for notify() call from Producer
//			}catch (InterruptedException e) {}
//		}
//		available=false;
//		notify();
		return seq;
	}
	
	public synchronized void put(int value) {
//		while(available==true) {
//			try {
//				wait();//waits for notify() from consumer
//			}catch (InterruptedException e) {}
//		}
		seq=value;
//		available=true;
//		notify();
	}
}

class Producer extends Thread{
	private CubbyHole cubbyHole;
	private int number;
	
	public Producer(CubbyHole c,int number) {
		cubbyHole=c;
		this.number=number;
	}
	
	public void run() {
		
		for(int i=0;i<10;i++) {
			cubbyHole.put(i);
			System.out.println("Producer#  "+this.number+"\tput:"+i);
			try {
				sleep((int)(Math.random()*100));
			}catch (InterruptedException e) {}
		}
	}
}

class Consumer extends Thread{
	private CubbyHole cubbyHole;
	private int number;
	
	public Consumer(CubbyHole c,int number) {
		cubbyHole=c;
		this.number=number;
	}
	
	public void run() {
		int value=0;
		for(int i=0;i<10;i++) {
			value=cubbyHole.get();
			System.out.println("Consumer#  "+this.number+"\tgot:"+value);
		}
	}
}

class ProducerConsumerTest {
	public static void main(String[] args) {
		CubbyHole c=new CubbyHole();
		Producer p1=new Producer(c, 1);
		Consumer c1=new Consumer(c, 1);
		p1.start();
		c1.start();
	}
}

其中,生产者和消费者通过CubbyHole对象共享数据,若像CubbyHole类中注释掉wait()和notify()方法,程序一结果如下:

Consumer#  1	got:0
Producer#  1	put:0
Consumer#  1	got:0
Consumer#  1	got:0
Consumer#  1	got:0
Consumer#  1	got:0
Consumer#  1	got:0
Consumer#  1	got:0
Consumer#  1	got:0
Consumer#  1	got:0
Consumer#  1	got:0
Producer#  1	put:1
Producer#  1	put:2
Producer#  1	put:3
Producer#  1	put:4
Producer#  1	put:5
Producer#  1	put:6
Producer#  1	put:7
Producer#  1	put:8
Producer#  1	put:9

说明,生产者和消费者的相对速度不能确定,这不是我们希望的那样:生产者写一个数,消费者就读这个数。所以,要对线程进行同步控制。例子中,重新启用注释部分代码,运行结果如下:

Consumer#  1	got:0
Producer#  1	put:0
Producer#  1	put:1
Consumer#  1	got:1
Producer#  1	put:2
Consumer#  1	got:2
Producer#  1	put:3
Consumer#  1	got:3
Producer#  1	put:4
Consumer#  1	got:4
Producer#  1	put:5
Consumer#  1	got:5
Consumer#  1	got:6
Producer#  1	put:6
Producer#  1	put:7
Consumer#  1	got:7
Producer#  1	put:8
Consumer#  1	got:8
Producer#  1	put:9
Consumer#  1	got:9

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值