进程与线程的概念
进程:操作系统中一个程序的执行周期称为一个进程。
在DOS系统的时代,由于其本身就是一个单进程的操作系统,所以在同一时间段上只能够有一个程序执行。后来发展到winodws系统后,我们发现多个程序可以同时执行,所以windows是一个多进程的操作系统。
线程:一个程序同时执行多个任务。通常,每一个任务就称为一个线程。与进程相比较,线程更"轻量级",创建、撤销一个线程比启动一个新进程开销要小的多。没有进程就没有线程,进程一旦终止,其内的线程也将不复存在。
多进程与多线程区别:本质区别在于,每个进程拥有自己的一整套变量,而线程则共享数据。共享变量使得线程之
间的通信比进程之间通信更有效、更方便。
那么,多线程表现在哪里呢?
在实际应用中,多线程非常有用。例如,一个浏览器应用可以同时下载多个图片、音乐;一个Web服务器需要同时处理多个并发的请求。这些都是多线程的应用。
高并发:访问的线程量非常非常高。
高并发带来的问题:服务器内存不够用,无法处理新的请求。
Java多线程实现
方法1:继承Thread类实现多线程
范例:
class MyThread extends Thread { // 线程主体类
private String title;
public MyThread(String title) {
this.title = title;
}
@Override
public void run() { // 所有线程从此处开始执行
for (int i = 0; i < 10; i++) {
System.out.println(this.title + ",i = " + i);
}
}
}
正确启动多线程使用start()方法而不是run()!!!
MyThread myThread1 = new MyThread("thread1") ;
myThread1.start();
方法2:Runnable接口实现多线程
Thread类的核心功能是进行线程的启动。如果一个类为了实现多线程直接去继承Thread类就会有但继承局限。在java中又提供有另外一种实现模式:Runnable接口。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
范例:
class MyThread implements Runnable { // 线程主体类
private String title;
public MyThread(String title) {
this.title = title;
}
@Override
public void run() { // 所有线程从此处开始执行
for (int i = 0; i < 10; i++) {
System.out.println(this.title + ",i = " + i);
}
}
}
范例:使用匿名内部类进行Runnable对象创建
public class TestDemo {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
}).start();
}
}
范例:使用Lamdba表达式进行Runnable对象创建
public class TestDemo {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println("Hello World");
new Thread(runnable).start();
}
}
Thread与Runnable区别
范例:使用Thread实现数据共享(产生若干线程进行同一数据的处理操作)
class MyThread extends Thread {
private int ticket = 10; // 一共10张票
@Override
public void run() {
while (this.ticket > 0) {
System.out.println("剩余票数:" + this.ticket--);
}
}
}
public class TestDemo {
public static void main(String[] args) {
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
此时启动三个线程实现卖票处理。结果变为了卖各自的票。
范例:使用Runnable实现共享
class MyThread implements Runnable {
private int ticket = 10; // 一共10张票
@Override
public void run() {
while (this.ticket > 0) {
System.out.println("剩余票数:" + this.ticket--);
}
}
}
public class TestDemo {
public static void main(String[] args) {
MyThread mt = new MyThread();
new Thread(mt).start();
new Thread(mt).start();
}
}
Runnable实现的多线程的程序类可以更好的描述出程序共享的概念。
方法3:Callable实现多线程:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
Runnable中的run()方法没有返回值,它的设计也遵循了主方法的设计原则:线程开始了就别回头。但是很多时候需要一些返回值,例如某些线程执行完成后可能带来一些返回结果,这种情况下就只能利用Callable来实现多线程。
class MyThread implements Callable<String> {
private int ticket = 10; // 一共10张票
@Override
public String call() throws Exception {
while (this.ticket > 0) {
System.out.println("剩余票数:" + this.ticket--);
}
return "票卖完了,下次吧。。。";
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<String> task = new FutureTask<>(new MyThread());
new Thread(task).start();
new Thread(task).start();
System.out.println(task.get());
}
}
在Thread类中提供有如下的线程名称方法:
获取当前线程名字方式:Thread.currentThread().getName();
通过以上程序我们发现,主方法本身就是一个线程,所有的线程都是通过主线程创建并启的。
疑问:进程在哪?
实际上每当使用了java命令去解释程序的时候,都表示启动了一个新的JVM进程。而主方法只是这个进程上的一个线程而已。
线程的五种状态之间的相互转换
线程休眠(sleep方法)
线程休眠:指的是让线程暂缓执行一下,等到了预计时间之后再恢复执行。
线程让步(yield()方法)
暂停当前正在执行的线程对象,并执行其他线程。
注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
join()方法(插队方法)
等待该线程终止。意思就是如果在主线程中调用该方法时就会让主线程休眠,让调用该方法的线程run方法先执行完毕之后在开始执行主线程。
线程停止
多线程中有三种方式可以停止线程。
1. 设置标记位,可以使线程正常退出。
2. 使用stop方法强制使线程退出,但是该方法不太安全所以已经被废弃了。
3. 使用Thread类中的一个 interrupt() 可以中断线程。
线程优先级
线程的优先级指的是,线程的优先级越高越有可能先执行,但仅仅是有可能而已。
设置优先级 public final void setPriority(int newPriority)
取得优先级 public final int getPriority()
1. 最高优先级:public final static int MAX_PRIORITY = 10;
2. 中等优先级:public final static int NORM_PRIORITY = 5;
3. 最低优先级:public final static int MIN_PRIORITY = 1;
线程是有继承关系的,比如当A线程中启动B线程,那么B和A的优先级将是一样的。