Java并发总结(一):线程基础

最近复习Java并发,写点东西总结总结。好记性不如烂博客。

并发

什么是并发?

与顺序编程不同,并发使得程序在同一时刻可以执行多个操作(宏观)。

为什么需要并发?

通常是为了提高程序的运行速度或者改善程序的设计。

线程

Java对并发编程提供了语言级别的支持。Java通过线程来实现并发编程。一个线程通常完成某个特定的任务,一个进程可以拥有多个线程,当这些线程一起执行的时候,就实现了并发。与操作系统中的进程相似,每个线程看起来好像拥有自己的CPU,但是其底层是通过切分CPU时间来实现的。与进程不同的是,线程并不是相互独立的,它们通常要相互合作来完成一些任务。

实现线程

你可以通过继承Thread类来实现自己的线程,关键的部分是,依靠重写run()方法来完成线程的工作。先写一个简单的线程,它的任务就是在控制台中依次打印出0,1,2,3,4。

public class SimpleThread extends Thread{
	@Override
	public void run() {
		for(int i = 0; i < 5 ;i++) {
			System.out.println(i);
			try {
				sleep(20); //让线程每打印一个数字之后休息20毫秒
			} catch (InterruptedException e) {
				System.out.println("Throw InterruptedException!");
			}
		}
	}
}


之后,我们启动两个这样的线程,让它们分别完成自己的任务。

public class ThreadTest {
	public static void main(String[] args) {
		SimpleThread simpleThread1 = new SimpleThread();
		SimpleThread simpleThread2 = new SimpleThread();
		simpleThread1.start();
		simpleThread2.start();
	}
}


执行结果如下:

0
0
1
1
2
2
3
3
4
4

结果似乎并不是像单线程程序那样,分先后两次分别打印出0,1,2,3,4,而是两个线程似乎同时在执行一样,都分别打印出自己的0,1,2,3,4,这便是“并行”。在调用一个Thread对象的start()方法之后,JVM就会启动一个新的线程。

直接继承Thread类并不是一个好的方法。因为有时候我们自己的线程类可能需要继承别的类,而Java并不支持多重继承。这个时候Runnable接口就有了用处(实际上Thread类本身也实现了Runnable接口)。我们要做的就是实现Runnable接口,并且重写run()方法。最后将一个实现了Runnable接口的类的实例交给Thread类的构造函数,就可以实例化出一个可用的线程了。

class MyTask implements Runnable {
        @override
        public void run() {
               //需要进行的操作
        }
}
 
//像下面这样来使用
MyTask mt = new MyTask();
Thread t = new Thread(mt);
t.start();

实际上,在很多时候,我们可以将一个线程看做一个任务,这个线程存在的价值就是为了完成某个任务。

休眠

正如第一个例子那样,我们可以让一个线程暂时休息一会儿。Thread类有一个sleep静态方法,你可以将一个long类型的数据当做参数传进去,单位是毫秒,表示线程将会休眠的时间。第一个例子中之所以调用了sleep方法,是因为打印5个数字的时间太短,如果不休眠的话可能看不到“并发”的效果,因为CPU的时间片还来不及转换,线程的任务就已经完成了。

让步

Thread类还有一个名为yield()的静态方法。这个方法的作用是为了建议当前正在运行的线程做个让步,让出CPU时间给别的线程来运行。程序中可能会有一个线程在某个时刻已经完成了一大部分的任务,并且这个时候让别的线程来运行比较合理。这样的情况下,就可以调用yield()方法进行让步。不过,调用这个方法并不能保证一定会起作用,毕竟它只是建议性的。所以,不应该用这个方法来控制程序的执行流程。

串入(join)

当一个线程t1在另一个线程t2上调用t1.join()方法的时候,线程t2将等待线程t1运行结束之后再开始运行。正如下面这个例子:

public class ThreadTest {
	public static void main(String[] args) {
		SimpleThread simpleThread = new SimpleThread();
 
		Thread t = new Thread(simpleThread);
 
		t.start();
 
	}
}
public class SimpleThread implements Runnable{
	@Override
	public void run() {
		Thread tempThread = new Thread() {
								@Override
								public void run() {
									for(int i = 10; i < 15 ;i++) {
										System.out.println(i);
									}
								}
							};
 
		tempThread.start();
 
		try {
			tempThread.join();        //tempThread串入
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
 
		for(int i = 0; i < 5; i++) {
			System.out.println(i);
		}
	}
}

输出结果为:

10
11
12
13
14
0
1
2
3
4


优先级

我们可以给一个线程设定一个优先级。线程调度器在做调度工作的时候,优先级越高的线程越可能得到先运行的机会。Thread类的setPriority方法和getPriority方法分别用来设置线程的优先级和获取线程的优先级。由于线程调度器根据优先级的大小来调度线程的效果在各种不同的JVM上差别很大,所以在绝大多数情况下,我们不应该依靠设定优先级来完成我们的工作,保持默认的优先级是一条很好的建议。

守护线程(deamon thread)

通常,程序中有一些线程的工作并不是不可或缺的,它只是用来协助其他线程来工作。这样的线程叫做守护线程或者后台线程。当进程中的所有非守护线程结束时,守护线程也就终止了,就算它还没有完全完成自己的任务。我们可以在一个线程启动之前调用setDaemon()方法来将这个线程设定成守护线程。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值