Java多线程

1 进程和线程
一个程序启动后就是一个进程,进程相当于一个空盒,它只提供资源装载的空间,具体的调度不是由进程完成的而是线程。
看如下这段代码

public class Test {
	public static void main(String[] args) {
		System.out.print(Thread.currentThread().getName());
	}
}	

它的打印结果如下
在这里插入图片描述
主函数创建的是一个名称为main的主线程,所有其他运行的线程都是它的子线程。
这也证明了进程的确只是一个提供资源装载的空间。

2 同步和异步

同步:排队执行。
异步:在同一时间可以执行多个任务。

3 线程具有随机性
如下代码

public class Test {
	public static void main(String[] args) {
		MyThread thread = new MyThread();
		thread.setName("thread");
		thread.start();
		for(int i = 0; i < 10000; i ++) {
		System.out.println("main=" + Thread.currentThread().getName());
		}
	}
}
class MyThread extends Thread{
	public void run() {
		for(int i = 0; i < 10000; i ++) {
			System.out.println("run=" + Thread.currentThread().getName());
		}
	}
}

打印结果如下
在这里插入图片描述
从这可以明显看出线程具有随机性,而造成这一现象的原因是因为CPU将时间片分给不同的线程,线程获取时间片后就执行任务,所以这些线程在交替地执行并输出,导致输出结果呈现乱序效果。由于线程间地切换需要耗时,因此,线程数过多反而会降低软件地执行效率。

4 使用Runnable接口实现多线程的优点。
因为Java是单根继承不支持多继承,所以为了改变这种限制,可以使用Runnable接口的方法来实现多线程技术。
如下面的代码

public class BServer1 extends AServer, Thread

很显然会报错,但如果改成这样

public class BServer1 extends AServer implements Runnable

就可行了

5 sleep(long millis)方法
sleep()方法是一个静态方法,作用是在指定的时间(毫秒)内让当前“正在执行的线程”休眠(暂停执行),这个“正在执行的线程”是指this.currentThread()返回的线程。

6 停止线程
停止线程是多线程开发很重要的一个技术点。
停止线程有三种方法:
1 使用退出标志使线程正常退出。
2 使用stop() 方法强行终止线程,但是这个方法不推荐使用,因为stop() 和 suspend() 、resume() 方法一样,都是作废过期的方法,使用它们可能发生不可预料的结果。
3 使用interrupt()方法中断线程。但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的终止。

异常法
通过判断线程interrupted()返回值是否为true来判断是否结束线程。
观察如下代码

public class MyThread extends Thread{
@Override 
	public void run() {
		super.run();
		try {
			for(int i = 0;i < 500000; i ++) {
				if(this.interrupted()) {
					System.out.println("已经是停止状态!");
					throw new InterruptedException();//也可使用return;
				}
				System.out.println("i=" + (i + 1));
			}
		}catch(InterruptedException ef) {
			System.out.println("线程结束啦");
			ef.printStackTrace();
		}
}
}

public class Run {
	public static void main(String[] args) {
		try {
			MyThread thread = new MyThread();
			thread.start();
			Thread.sleep(2000);
			thread.interrupt();
		}catch(InterruptedException ef) {
			ef.printStackTrace();
		}
			System.out.println("end");
		
	}
}
/*注:请不要将循环次数减少过多,否则程序很有可能在运行完thread后,才运行主线程main,导致看起来就
好像用了run()而非start()方法*/
/*此外,interrupted()方法会清除线程状态,即如果一个线程被interrupt()后,那么执行一次
interrupted()(此时判定为true) 就会在第二次interrupted()判定为false isInterrupted()方法可以在不清除状态的情况下进行判断*/

结果如下
在这里插入图片描述

interrupt()方法只是给线程赋予了一个变量,通过判断这个变量的值来判断是否停止一个线程。真正意义上停止线程的方法是下面的throw new InterruptedException();或者return;
此外interrupt()和sleep()方法碰到一起就会出现异常。此外,异常法相比于return;的好处在于,当被打断时可以减少你打印异常的次数。特别是你存在许多线程可以打断此线程时。避免冗余。

7 暂停线程
暂停线程意味着此线程还可以回复运行。在多线程中可以使用suspend() 方法暂停线程,使用resume() 方法来恢复线程的执行。
但是suspend()resume() 方法使用不当,极易造成公共同步对象被独占,其他线程无法访问公共同步对象的结果。即该线程暂停时,如果你给线程添加了锁那么之后带有这个锁的线程都会停止。这也就导致了很可能最后得到的数据不完整。
因此,这两个方法已经作废了。想要实现暂停和恢复处理可以使用wait() notify() 或 notifyAll() 方法
注(println()也是一个带锁的方法,其带有关键字synchronized))

8 yield() 方法
yield() 方法的作用是放弃当前的CPU资源,让其他任务区占用CPU执行时间,放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。
如下例

public class MyThread_1 extends Thread {
	public void run() {
		long beginTime = System.currentTimeMillis();
		int  count = 0;
		for(int i = 0; i < 5000000; i ++) {
			//Thread.yield();
			count += (i + 1);
		}
		long endTime = System.currentTimeMillis();
		System.out.println("用时" + (endTime - beginTime) + "毫秒!");
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		MyThread_1 thread = new MyThread_1();
		thread.start();
	}

}

当前代码运行结果如下在这里插入图片描述

在这里插入图片描述

这是运行两次的结果

添加 Thread.yield()后运行两次的结果如下

在这里插入图片描述

在这里插入图片描述

放弃的时间是具有随机性的。

9 线程的优先级

在操作系统中,线程可以划分优先级,优先级较高的线程得到CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务,其实就是让高优先级的线程获得更多的时间片。
设置线程优先级有助于“线程规划器“确定在下一次选择哪一个线程来优先执行。
设置线程的优先级使用setPriority()方法,在Java中线程的优先级分为1~10 共10 个等级,除此之外的数字都会报错。
在java中线程的优先级具有继承性,例如A线程启动B线程,则B线程的优先级与A线程是一样的。
高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完。

public class Priority_learning {
		public static void main(String[] args) {
		// TODO Auto-generated method stub
			for(int i = 0; i < 6; i ++) {
				MyThread1 thread1 = new MyThread1();
				thread1.setPriority(10);
				thread1.start();
				
				MyThread2 thread2 = new MyThread2();
				thread2.setPriority(4);
				thread2.start();
			}
	}

}
class MyThread1 extends Thread{
	long beginTime = System.currentTimeMillis();
	long addResult = 0;
	public void run() {
	for(int j = 0; j < 10; j ++) {
		for(int i = 0; i < 50000; i ++) {
			Random random = new Random();
			random.nextInt();
			addResult += i;
		}
	}
	long endTime = System.currentTimeMillis();
	System.out.println("*****thread1 use time =" + (endTime - beginTime));
	}
}
class MyThread2 extends Thread{
	long beginTime = System.currentTimeMillis();
	long addResult = 0;
	public void run() {
	for(int j = 0; j < 10; j ++) {
		for(int i = 0; i < 50000; i ++) {
			Random random = new Random();
			random.nextInt();
			addResult += i;
		}
	}
	long endTime = System.currentTimeMillis();
	System.out.println("*****thread2 use time =" + (endTime - beginTime));
	}
}

在这里插入图片描述

此时由于优先级差距大且运行结果少,因此看起来好像是先运行优先高的然后运行优先级低的。(顺序性)
但是如果我们将一个设置为10,另一个设置为9,此时结果就显得很有随机性。
在这里插入图片描述

10 守护线程
Java中有两种线程,一种是用户线程,也称非守护线程;另一种是守护线程。
当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程是垃圾回收线程。最典型的应用就是GC(垃圾回收器),即守护线程退出,进程也随即结束了。凡是调用setDaemon(true)代码并且传入true值得线程才是守护线程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值