程序(软件):数据和指令的集合。软件架构:B/S C/S 软件分类:系统软件,应用软件。
进程:正在运行的程序,会在内存中分配空间。
线程:进程中的多条路径。
多线程是指有多条线程并发的执行。
并发:多条线程在同一时间段内交替执行。
并行:多条线程同时执行。
创建线程:
1.继承:extends Thread类 继承的子类需要重写run方法
2.实现:implements Runnable接口
注意:无论是继承还是实现,直接调用重写过的run方法是无法开启多线程的,jvm中默认start()方法开启多线程,start()方法会默认调用重写的run()方法。
3.线程创建方式的优缺点:
① 继承方式写法简单。实现方式写法相对比较复杂
② 继承与实现都是通过 start 方法才能开启线程。
③ 继承的扩展性和灵活性都低于实现方式的扩展性和灵活性
④ 继承的方式,耦合度高于实现的方式。
实际开发中,使用实现形式要多于继承形式。
使用继承来开启多线程 :代码示例:
1 package com.Allen.Thread;
2
3 public class Demo5 {
4 public static void main(String[] args) {
5 ExtendsThread et=new ExtendsThread();
6 et.start();
7 for(int j=0;j<100;j++) {
8 System.out.println("=========j"+j);
9 }
10 }
11 }
12 class ExtendsThread extends Thread{
13 @Override
14 public void run() {
15 for (int i = 65; i <91; i++) {
16 System.out.println((char)i);
17 }
18 }
19 }
执行结果:
可以看到两个线程在交替运行。
使用实现Runnable接口来开启多线程:代码示例:
1 package com.Allen.Thread;
2
3 public class Demo6 {
4
5 public static void main(String[] args) {
6 ImplementsRunnable ir=new ImplementsRunnable();
7 Thread td=new Thread(ir);
8 td.start();
9 for (int j = 0; j < 1000; j++) {
10 System.out.println("====j"+j);
11 }
12 }
13 }
14 class ImplementsRunnable implements Runnable{
15
16 @Override
17 public void run() {
18 for (int i = 0; i < 1000; i++) {
19
20 System.out.println("***********i"+i);
21 }
22 }
23 }
执行结果:
实现Runnable接口来开启多线程还可以使用匿名对象 代码示例:
1 package com.Allen.Thread;
2
3 public class Demo6 {
4
5 public static void main(String[] args) {
6
7 Thread td=new Thread(new ImplementsRunnable()); //这里使用的是匿名对象
8 td.start();
9 for (int j = 0; j < 1000; j++) {
10 System.out.println("====j"+j);
11 }
12 }
13 }
14 class ImplementsRunnable implements Runnable{
15
16 @Override
17 public void run() {
18 for (int i = 0; i < 1000; i++) {
19
20 System.out.println("***********i"+i);
21 }
22 }
23 }
线程的执行原理:
线程的执行:CPU的抢占式的调度模式,抢占CPU的时间片
不同的线程不共享栈空间,需要开辟栈空间单独运行
线程的生命周期:
***1. 线程的生命周期 / 线程的状态
① 生命周期:从事物的出生,到消亡的过程。
***② 线程生命周期的状态:
1 》初始状态 : 创建了线程对象,但是没有调用 start 方法开启线程。
2 》可运行状态: 调用了 start 方法,到线程队列中排队,抢占 cpu 的时间片。但是还没有抢占上
。
3 》运行状态: 抢到了 cpu 的时间片。正在执行。
4 》 终止状态: cpu 时间片之内完成了线程的执行。
5 》 阻塞状态: cpu 时间片执行期间,遇到了意外的情况。
③ 线程的生命周期,指同一个线程在不同时期的状态。
④ java 中,线程的生命周期通过类来表示不同的状态 : Thread.State
new
至今尚未启动的线程处于这种状态。
runnable
正在 Java 虚拟机中执行的线程处于这种状态。
blocked
受阻塞并等待某个监视器锁的线程处于这种状态。
waiting
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
timed_waiting
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
terminated
已退出的线程处于这种状态。
线程的常用方法;
1.getName()返回线程的名称
2.currentThread()获取当前线程的引用对象。
线程的名称 main方法所在的线程为主线程 ,线程名为main
自定义的线程:Thread-0,Thread-1 默认格式:Thread-index 其中index从0开始 线程的名称可以设置
3.setName () 设置线程的名称
4.***getpriority()/setPriority() 返回线程的优先级/设置线程的优先级
***线程的优先级:从1到10,默认为5,最高优先级为10,最低优先级为1
线程如果优先级越高,抢占CPU时间片的可能性越大,默认一个线程创建时,优先级为5.
5.isDaemon()/setDeamon(true) 判断该线程是否为守护线程/设置线程为守护线程,参数值为true。
守护线程是用来守护用户线程,为用户线程进行准备或者守护的工作。
随着用户线程的消亡,守护线程无论是否执行完都会随着用户线程消亡。
6.join()等待该线程的终止,相当于用户调用。
7****sleep(ms) 休眠,毫秒值
8.start()开启线程的方法,会默认调用run()方法,进行线程的开启。
9.yield()退出当前正在执行的线程,让给其他线程执行,线程的让步。
守护线程的代码示例:
1 package com.Allen.Thread;
2
3 public class Demo7 {
4
5 public static void main(String[] args) {
6 DaemonThread dh=new DaemonThread();
7 dh.setDaemon(true);
8 dh.start();
9 for (int j = 0; j <20; j++) {
10 System.out.println("****"+j);
11 }
12 }
13
14 }
15 class DaemonThread extends Thread{
16 @Override
17 public void run() {
18 for (int i = 0; i < 100; i++) {
19
20 System.out.println("=================="+i);
21 }
22 }
23 }
执行结果:
本例DaemonThread线程要打印倒99,但是随着主线程的消亡,守护线程也消亡了,但是为什么主线程 到19就消亡了,而守护线程却到了28呢?这是因为有延迟,不能立即结束。
主线程和守护线程的关系就像人与影子的关系,共存亡。
线程的安全问题:
1.多线程:多个线程并发执行。
2.多线程在执行时,会引发安全问题
-
通过银行账户取款的模拟来演示多线程的安全问题。
-
多线程的安全问题:
多个线程共同访问共享资源 ( 共有的资源 ) 时,由于线程的调度机制是抢占式调度,可能会发生多个线程在执行时,
同时操作共享资源,导致程序执行结果与预期不一致的现象。 -
解决多线程的安全问题:
1> 解决方式一:同步:但是该方式会导致效率低,相当于单个线程执行。
a) 同步方式: ( 加锁 )
① 同步方法 : 相当于同步代码块中的 this 锁。
格式 :
权限修饰符 修饰符 synchronized 方法的返回值 方法的名称 ( 参数 )
② 同步代码块:
1 》 同步锁对象 :任意对象
格式 : synchronized ( 任意对象 ){
加锁的代码
}
2 》同步 this 锁:
③ 同步代码块和同步方法加锁时,需要注意:
《 1 》 加锁必须是给共享资源加的。
《 2 》 必须加的是同一把锁。
多线程安全问题的代码示例:
1 package com;
2 /**
3 1.多线程 : 多个线程并发执行。
4 2.多线程在执行时,会引发安全问题。
5 3.通过银行账户取款的模拟来演示多线程的安全问题。
6 4.多线程的安全问题:
7 多个线程共同访问共享资源(共有的资源)时,由于线程的调度机制是抢占式调度,可能会发生多个线程在执行时,
8 同时操作共享资源,导致程序执行结果与预期不一致的现象。
9 5.解决多线程的安全问题:
10 1>解决方式一:同步:但是该方式会导致效率低,相当于单个线程执行。
11 a)同步方式:(加锁)
12 ① 同步方法: 相当于同步代码块中的this锁。
13 格式 :
14 权限修饰符 修饰符 synchronized 方法的返回值 方法的名称(参数)
15 ② 同步代码块:
16 1》 同步锁对象 :任意对象
17 格式 :synchronized (任意对象){
18 加锁的代码
19 }
20 2》同步this锁:
21 ③ 同步代码块和同步方法加锁时,需要注意:
22 《1》 加锁必须是给共享资源加的。
23 《2》 必须加的是同一把锁。
24 */
25 public class 多线程的安全问题 {
26 public static void main(String[] args) {
27 Account a=new Account();
28 //多线程
29 Thread t=new Thread(a);
30 Thread t1=new Thread(a);
31 t.setName("张三");
32 t1.setName("李四");
33 t.start();
34 t1.start();
35 }
36 }
37 class Account implements Runnable{
38 double balance=1000;//账户余额
39 Object obj=new Object();//任意对象
40 String s=new String();
41 //同步方法
42 @Override
43 public /*synchronized*/ void run() {
44 //同步锁对象,任意对象。
45 synchronized (this) {//this锁
46 //取款
47 if (balance > 0) {
48 //休眠
49 try {
50 Thread.sleep(1000);
51 } catch (InterruptedException e) {
52 // TODO Auto-generated catch block
53 e.printStackTrace();
54 }
55 balance -= 1000;
56 System.out.println(Thread.currentThread().getName() + "取款后,金额为" + balance);
57 } else {
58 System.out.println(Thread.currentThread().getName() + "余额不足");
59 }
60 }
61 }
62 }
======================================================================
1 package com;
2 /**
3
4 1.多线程的安全问题: ① 多个线程 ② 共享资源。
5 2.同步: 判断多个线程是否加的是同一把锁。
6 ① 同步方法
7 ② 同步代码块:
8 1》 锁对象(任意对象)
9 2》this锁
10 3》静态锁 (静态对象)
11 4》类锁(.class)
12 */
13 public class 继承方式下的多线程的安全问题 {
14 public static void main(String[] args) {
15 AccountThread a=new AccountThread();
16 AccountThread a1=new AccountThread();
17 a.setName("张三");
18 a1.setName("李四");
19 a.start();
20 a1.start();
21
22 }
23 }
24 class AccountThread extends Thread{
25 static double balance=1000;//账户余额
26 //静态锁
27 static Object o=new Object();
28 //同步方法
29 @Override
30 public void run() {
31 //类锁
32 synchronized (AccountThread.class) {//this
33 //取款
34 if (balance > 0) {
35 //休眠
36 try {
37 Thread.sleep(1000);
38 } catch (InterruptedException e) {
39 // TODO Auto-generated catch block
40 e.printStackTrace();
41 }
42 balance -= 1000;
43 System.out.println(Thread.currentThread().getName() + "取款后,金额为" + balance);
44 } else {
45 System.out.println(Thread.currentThread().getName() + "余额不足");
46 }
47 }
48 }
49 }
PS:alt+方向键可以上下平移代码
ctrl+加减号可以调节eclipse的字体大小