多线程(上)

多线程

一、进程与线程

1、进程

    操作系统中一个程序的执行周期称为一个进程。每个进程中至少包含一个线程。

2、线程

进程中负责程序执行的执行单元。线程本身依靠程序进行运行,线程是程序中的顺序控制流。

与进程相比,线程更“轻量级”,创建、撤销一个线程比启动一个新进程开销小得多;没有进程就没有线程,进程一旦终止,其内的线程也不复存在。

3、单线程

程序中只包含一个线程,主方法其实就是一个主线程。

4、多线程

在一个程序中运行多个任务,目的是更好的利用资源。

5、线程的状态


二、多线程的实现

1、继承Thread

ThreadJava.lang包中定义,在继承Thread必须覆写run()方法。

(1)创建线程

//线程主体类
class MyThread extends Thread{
	private static int num = 0;
	public MyThread(int num) {
		num++;
	}
	//所有线程从该处开始执行
	public void run() {
		System.out.println("主动创建的第 "+num+"个线程");
	}
}

(2)创建线程对象启动线程

错误的多线程启动方式:

   在有了线程的情况下,很自然会产生线程类的实例化对象再调用其的run()方法。实则我们不能直接调用run()方法,run()方法的调用只会做出顺序打印,和多线程无关。

public class MoreThread4_25 {
	public static void main(String[] args) {
		Thread mThread1 = new MyThread("第一个线程");
		Thread mThread2 = new MyThread("第二个线程");
		Thread mThread3 = new MyThread("第三个线程");
		mThread1.run();
		mThread2.run();
		mThread3.run();
	}
}

           

正确的多线程启动方式:

      实则我们应该使用Thread类中的start()方法来启动线程。

因为public synchronized void start()此方法会自动调用线程的run()方法。

public class MoreThread4_25 {
	public static void main(String[] args) {
		Thread mThread1 = new MyThread("第一个线程");
		Thread mThread2 = new MyThread("第二个线程");
		Thread mThread3 = new MyThread("第三个线程");
		mThread1.start();
		mThread2.start();
		mThread3.start();
	}
}

重复执行则会发现,所有的线程对象交替执行

                                 

(3)start()方法和run()方法调用的区别

class MyThread extends Thread{
	private String info;
	public MyThread(String info) {
		this.info = info;
	}
	//所有线程从该处开始执行
	public void run() {
		System.out.println("主动创建的第  \""+info+"\" 个子线程的ID:"+Thread.currentThread().getId());
	}
}
public class MoreThread4_25 {
	public static void main(String[] args) {
		System.out.println("主线程ID:"+Thread.currentThread().getId());
		Thread mThread1 = new MyThread("第一个线程");
                Thread mThread2 = new MyThread("第二个线程");
                //正确的多线程启动方式
                mThread1.start();
                //错误的多线程启动
                mThread2.run();
	}
}

        

从上图输出结果可以看出:

Thread1Thread2的线程ID不同,Thread2和主线程的ID相同,此时表明run()方法的创建并不会创建新的线程,而是在主线程中运行run()方法,和普通方法没有什么区别。

Thread1start()方法在Thread2run()方法之前调用,但是运行结果是先输出Thread2run()方法的ID,说明新建创建的线程不会影响主线程后续的执行。

2、Runnable()接口实现多线程

Java中创建线程除了继承Thread类(具有单继承局限)外,还可以通过实现Runnable()接口来实现类似的功能,但是也要覆写run()方法。

1)实现Runnable接口

class MyRunnable implements Runnable{
	private String info;
	public MyRunnable(String info) {
		this.info = info;
	}
	@Override
	public void run() {
		System.out.println("新建线程名:"+this.info+",ID:"+Thread.currentThread().getId());
	}
	
}
public class MoreThread4_25{
	public static void main(String[] args) {
		System.out.println("主线程ID:"+Thread.currentThread().getId());
		MyRunnable myRunnable = new MyRunnable("线程1");
		//且此时MyThRunnable类继承的不再是Thread类而实现了Runnable接口
		//虽然解决了单继承局限问题,但是没有start()方法被继承了。
		//那么此时就需要使用到Thread类提供的构造方法。
		Thread thread = new Thread(myRunnable);
		thread.start();
	}
}

                        

通过实现Runnable接口来实现多线程实则是:

   我们定义了一个子任务,然后交给Thread去执行。

   这种实现多线程的方式必须将Runnable作为Thread的参数,仍然是利用Thread类的start()方法创建新线程,完成后续工作,和Thread类似若直接调用其的run()方法和普通方法没什么区别,并不会创建新的线程。

2Runnable接口对象的内部类和Lambda表达式实现

/*
 * Runnable接口对象匿名内部类、Lambda表达式实现
 * */
public class MoreThread4_25{
	public static void main(String[] args) {
		System.out.println("主线程ID:"+Thread.currentThread().getId());
		//内部类实现Runnable的接口对象
		new Thread(new Runnable() {
			private String info = "线程1";
			@Override
			public void run() {
				System.out.println("新建线程名:"+this.info+",ID:"+Thread.currentThread().getId());
			}
		}).start();
		//Lambda表达式实现Runnable的接口对象
		Runnable runnable = () ->{
			String info = "线程2";
			System.out.println("新建线程名:"+info+",ID:"+Thread.currentThread().getId());
		};
		new Thread(runnable).start();
	}
}

                       

3、ThreadRunnable区别

Runnable接口实现多线程要比Thread类好,因为避免了单继承的局限,Thread类是Runnable接口的子类,所以Thread类一定覆写了Runnablerun()方法。且使用Runnable实现的多线程的程序类可以更好的描述出程序共享的概念。

线程类的继承结构:代理设计模式

                      

(1)使用Thread类实现数据共享

class MyThread extends Thread{
	private int ticketNum = 5;
	public void run() {
		while(this.ticketNum > 0) {
			System.out.println("剩余票数: "+this.ticketNum--);
		}
	}
}
public class MoreThread4_25{
	public static void main(String[] args) {
		new MyThread().start();
		new MyThread().start();
		new MyThread().start();
	}
}

                         

根据结果可以发现启动三个线程,各自处理各自的数据而没有实现共同处理同一组数据。

要想达到共同处理同一组数据,应将ticketNum置为static变量。

(2)使用Runnable实现数据共享

class MyRunnable implements Runnable{
	private int ticketNum = 5;
	public void run() {
		while(this.ticketNum > 0) {
			System.out.println("剩余票数: "+this.ticketNum--);
		}
	}
}
public class MoreThread4_25{
	public static void main(String[] args) {
		MyRunnable myRunnable = new MyRunnable();
		new Thread(myRunnable).start();
		new Thread(myRunnable).start();
		new Thread(myRunnable).start();
	}

}

                        

 Runnable实现的多线程的程序类可以更好的描述出程序共享的概念


4、Callable实现多线程

JDK1.5开始追加了新的开发包:java.uti.concurrent。这个开发包主要是进行高并发编程使用的,包含很多在高并发操作中会使用的类。在这个包里定义有一个新的接口Callable

Runnable中的run()方法没有返回值,它的设计也遵循了主方法的设计原则:线程开始了就别回头。但是很多时候需要一些返回值,例如某些线程执行完成后可能带来一些返回结果,这种情况下就只能利用Callable来实现多线程。

class MyCallable implements Callable<String>{
	private int ticketNum = 5;
	public String call() throws Exception {
		while(this.ticketNum > 0) {
			System.out.println("剩余票数: "+this.ticketNum--);	
		}
		return "不好意思,刚好没票";
	}
}
public class MoreThread4_25{
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		FutureTask<String> task = new FutureTask<>(new MyCallable());
		new Thread(task).start();
		new Thread(task).start();
		System.out.println(task.get());
	}
}
                        

 
 

猜你喜欢

转载自blog.csdn.net/qq_40409115/article/details/80073300