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值得线程才是守护线程。