早期的操作系统,建立在了所谓的cpu,存储器,IO设备之上,但是最为宝贵的便是cpu,计算机发展至今,硬盘已经从几M发展到现在的几TB,cpu仍在不紧不慢发展,作为计算机的大脑,cpu可谓肩负重任,早期的操作系统,由于系统的缺陷,cpu只能运行单道作业,如果要等待IO,便会一直等下去,直到我们使用完IO之后,才会响应,这便是所谓的单道作业的缺陷!

随着操作系统发展,我们将cpu分的越来越详细,从最初的一个程序–>作业–>进程–>线程,个人认为是由于cpu的发展跟不上我们的需求,才会促使我们将cpu更为紧细的利用,直到现在的多核多线程,我们便开始专心研究多线程,以提高任务效率,而不是促使我们的应用程序崩溃!

线程

线程来源于一个进程,但是由于进程切换开销大,为了提高cpu的利用率,所以我们便将单个进程分为多个线程,多个线程可以并发或者并行运行,各线程在运行过程中会因为访问共享数据及等待数据而发生互斥与同步的关系
多线程可以使程序反应更快,交互性更强,执行效率更高

创建一个线程

Java中的程序,我们通常从main方法开始执行,jvm便会给main创建一个线程,从上到下开始执行main方法
当Java程序作为applet开始运行的时候,web浏览器便会启动一个线程来运行applet

实现Runnable接口

通常我们采用该方法来实现多线程

  1. 实现Runnable接口以及相应的run方法
  2. 创建一个Thread,并分配任务给Thread
  3. 调用Thread实例的start方法开启线程(不可以调用run方法,如果调用,只是在当前线程执行run方法,并不会产生多线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Test implements Runnable{


public static void main(String[] args) {

Test test = new Test();
Thread thread = new Thread(test);
thread.start();

}

/*这里的run方法指明如何完成这个任务,如果使用新线程来执行,Java虚拟机会自动调用该方法*/
@Override
public void run() {

while(true){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1 = sdf.format(date);
System.out.println(date1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

继承Thread

由于JAVA是单继承,我们经常不使用该方法来实现多线程,并且继承Thread,会让我们将任务和运行任务的机制混在了一起

  1. 继承Thread
  2. 重写run方法
  3. 使用当前类的实例调用start方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Test extends Thread{



public static void main(String[] args) {
Test test = new Test();
test.start();

while(true){
System.out.println(new Date());
}


}


@Override
public void run() {
while(true){
System.out.println("A");
}
}
}

错误的使用sleep方法

sleep()方法将会让调用该方法的线程进入休眠状态,因此如果我们错误的使用该方法,将会影响程序的健壮性!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Third implements Runnable{

@Override
public void run() {


for(int i = 0; i < 100; i++){
System.out.println("A");
}

try {
/*当一个休眠线程的interrupte()被调用,就会产生这样一个异常*/
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}



/**
* 使用这种方法来抛出异常是错误的
*/
try{
while(true){
Thread.sleep(1000);
}
}catch(InterruptedException e){
e.printStackTrace();
}

/**
* 使用这种方法来抛出异常是正确的
*/
while( true ){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

}

由于使用stop(),以及suspend(),resume(),存在不安全因素,现在的JDK并不提倡使用这些方法,为了替代stop()方法,我们可以通过给Thread变量赋值null来终止当前线程


使用yield()方法为其他线程让出cpu时间

yield()方法可以为其他线程临时让出cpu时间,对于可能出现死锁的程序,我们可以使用该方法来避免

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Test2 implements Runnable{



public static void main(String[] args) {
Test2 test = new Test2();
Thread thread = new Thread(test);
thread.start();

Test2 test1 = new Test2();
Thread thread1 = new Thread(test1);
thread1.start();


/*设置thread1线程的优先级最高,因为JVM总是从优先级最高的线程开始执行 */
thread1.setPriority(Thread.MAX_PRIORITY);
}


@Override
public void run() {
while(true){
/*输出当前执行的线程的ID*/
System.out.println(Thread.currentThread().getId());
/*为其他线程临时让出CPU时间*/
Thread.yield();
}
}
}

线程优先级

众所周知,cpu在线程中采用时间片轮转算法,JVM也是如此,将会执行优先级最高的线程,较低的优先级会使得当前线程再有在没有比它优先级更高的线程的时候才会执行,如果优先级相同,系统会将线程排到一个循环队列中等待,然后进行cpu调度,一个一个开始执行

资源竞争(缺乏状态)

如果总有一个优先级较高的线程在运行,或者有一个相同优先级的程序不愿意退出,那么这个线程可能永远也没有运行的机会,称为资源竞争

避免方法(给低优先级或者相同优先级的线程一个运行程序的机会)
  1. 调用线程的yield方法,可以使得线程临时让出cpu资源
  2. 调用线程的sleep方法

闪烁文本

动画的设置

定时器和一个线程都可以控制动画,但是使用定时器(一个源组件,以“固定的速率触发一个ActionEvent时间,发生动作时间,定时器会调用监听器的actionPersormed方法来处理这个事件,定时器和事件处理都处于同一事件分发线程上”)

如果处理这个事件需要花费很长时间,那么两个事件之间真正的延时时间将比请求延时时间更长,这种情况下,萤爱在一个独立的线程上运行事件处理

通常,线程比定时器更加可靠,响应速度更快,如果需要精确的延迟时间或者快速相应,最好使用线程,否则,使用定时器会比线程更加简单,也更加有效,因为定时器运行在GUI事件分发线程上,所以,定时器比线程占用的系统资源少,故而不需要为定时器创建新线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class FlashingText  extends JApplet implements Runnable{


private JLabel label = new JLabel("Welcome", JLabel.CENTER);


public FlashingText() {
add(label);
new Thread(this).start();

}

@Override
public void run() {
try{
while(true){
if(label.getText() == null)
label.setText("Welcome");
else
label.setText(null);
}
}catch(Exception e){
e.printStackTrace();
}
}

}