线程、同步代码块、同步函数、死锁

1.线程
是"进程"中某个单一顺序的控制流。也被称为轻量进程(lightweight processes)。计算机科学术语,指运行中的程序的调度单位。

一个进程必须至少有一个线程,通常称为主线程。

2.线程调度
计算机通常只有一个CPU,在任意时刻只能执行一条机器指令,每个线程只有获得CPU的使用权才能执行指令.所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得CPU的使用权,分别执行各自的任务.在运行池中,会有多个处于就绪状态的线程在等待CPU

JAVA虚拟机的一项任务就是负责线程的调度,线程调度是指按照特定机制为多个线程分配CPU的使用权.

Windows的调度是以线程为粒度调度的。调度的决策被严格限制在以线程为基础,并不考虑这个线程属于哪一个进程。当考虑到进程并不运行,而仅为其线程提供资源和运行的上下文环境时,这种方法就有意义了,例如,进程A有10个可运行的线程,进程B有2个可运行的线程,而且这12个线程的优先级别相同,那么,每一个线程将会使用1/12的CPU时间,而不是将CPU 50%的时间分配给进程A,50% 的时间分配给进程B。

有两种调度模型:分时调度模型和抢占式调度模型。
分时调度模型是指让所有的线程轮流获得cpu的使用权,并且平均分配每个线程占用的CPU的时间片这个也比较好理解。

java虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃CPU。

一个线程会因为以下原因而放弃CPU:
1. java虚拟机让当前线程暂时放弃CPU,转到就绪状态,使其它线程获得运行机会。
2. 当前线程因为某些原因而进入阻塞状态。
3. 线程结束运行。

需要注意的是,线程的调度不是跨平台的,它 不仅仅取决于java虚拟机,还依赖于操作系统。在某些操作系统中,只要运行中的线程没有遇到阻塞,就不会放弃CPU;在某些操作系统中,即使线程没有遇到阻塞,也会运行一段时间后放弃CPU,给其它线程运行的机会。
java的线程调度是不分时的,同时启动多个线程后,不能保证各个线程轮流获得均等的CPU时间片。

如果希望明确地让一个线程给另外一个线程运行的机会,可以采取以下办法之一。
调整各个线程的优先级
让处于运行状态的线程调用Thread.sleep()方法
让处于运行状态的线程调用Thread.yield()方法
让处于运行状态的线程调用另一个线程的join()方法

3.例子
[java]  view plain copy
  1. public class Demo {  
  2.     public static void main(String[] args) {          
  3.         new Test();  
  4.         new Test();  
  5.         new Test();  
  6.         new Test();  
  7.         System.gc();    //调用gc  
  8.         System.out.println("hello world");        
  9.     }  
  10. }  
  11. class Test  
  12. {  
  13.     public void finalize(){  
  14.         System.out.println("test clear");  
  15.     }  
  16. }  
/**
反复运行并观察输出,其结果是不确定的。因为main主线程、垃圾回收线程,它们线程优先级相同,随机占用cpu
*/

4.创建线程方式一
1)定义类继承Thread类,覆盖run方法(线程所要执行的代码),将业务代码写在run方法中。

2)通过实例化,来创建线程对象,通过start启动线程,调用线程任务run方法

3)run和start区别
run()方法:在本线程内调用该Runnable对象的run()方法,可以重复多次调用; (Thread类也是继承Runnable接口)
start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程;

4)getName()与Thread.currentThread().getName()
每个继承Thread类的子类,在创建时都有自己的线程名称,都有getName()方法;
run函数中,getName()和Thread.currentThread().getName()是有区别的,前者是线程对象的名称,后者是当前运行线程的名称

5)例子
example01
[java]  view plain copy
  1. public class Demo2 {  
  2.     public static void main(String[] args) {  
  3.         Test2 t1=new Test2();  
  4.         Test2 t2=new Test2();  
  5.         //t1.run(); //不开启线程,仅是对象调用方法。              
  6.         //t2.run();  
  7.         t1.start();  
  8.         t2.start();  
  9.         System.out.println(Thread.currentThread().getName());  
  10.     }  
  11. }  
  12. class Test2 extends Thread  
  13. {     
  14.     public void run(){  
  15.         for(int i=1;i<=10;i++){  
  16.             System.out.println(Thread.currentThread().getName());  
  17.             //System.out.println(getName());        //run或start结果一样,对象名  
  18.         }  
  19.     }  
  20. }  
example02
[java]  view plain copy
  1. public class Demo1 {      
  2.     public static void main(String[] args) {  
  3.         Ticket t1=new Ticket();  
  4.         Ticket t2=new Ticket();  
  5.         Ticket t3=new Ticket();  
  6.         Ticket t4=new Ticket();  
  7.         t1.start();  
  8.         t2.start();  
  9.         t3.start();  
  10.         t4.start();       
  11.     }  
  12. }  
  13. class Ticket extends Thread  
  14. {  
  15.     private static int num=10;  
  16.       
  17.     public void run(){  
  18.         while(num>0){  
  19.             System.out.println(getName()+":"+--num);  
  20.         }  
  21.     }  
  22. }  
/**
 输出结果可能不是有序的,原因多个线程同时执行,某个线程可能--num后,将值传给println方法,
 执行println方法时,还没有来得及打印,但失去执行权,后来某个时刻又获取到执行权
 */
6)每条线程有自己的栈空间,意味着每个线程都有自己的局部变量

5.线程的五种状态

创建、运行、消亡(run方法结束或stop())、睡眠(sleep)、等待(wait方法,对应notify唤醒方法)

1)cpu的执行资格:可以被cpu处理的线程,在处理队列中
2)cpu的执行权:正在被cpu处理
3)睡眠和等待,设置线程执行资格与执行权
4)运行:既具备执行资格、又有执行权
5)cpu处理某线程时,该线程具备执行资格,执行权,其它线程不具备执行权,但具备执行资格,处于临时阻塞状态,等待执行权
6)睡眠和等待,都不具备

6.创建线程方式二
1)定义类实现Runnable接口,覆盖run方法,将线程的任务代码封装到run中
2)通过Thread创建线程对象,并将Runnable子类对象作为构造函数的参数传递,线程对象调用start方法。

7.两种创建对比
继承Thread与实现Runnable接口,前者具备线程所有功能,强调is-a的关系,后者仅仅提供线程任务功能,强调has-a
Runnable好处:避免单继承的局限性,将任务从线程中的子类中分离,将任务封装成对象

8.多线程安全问题
多个线程操作(修改)共享的数据,可能存在安全问题。(例如run中的静态变量)

 主要是多线程做同样的事,操作同一个run函数。

9.同步代码块
synchronized(对象)
{

}
好处:解决线程的安全问题
弊端:都会判断同步锁,降低效率
使用前提:多个线程使用同一个锁 ,(同一个对象同一个锁。所以对象写为new Object())

===============================分割线==========================

1.同步函数

[java]  view plain copy
  1. class Bank implements Runnable {  
  2.     private int sum;  
  3.     private Object obj = new Object();  
  4.   
  5.     public void add(int num) {  
  6.         synchronized (obj) {  
  7.             sum += num;       
  8.             System.out.println("sum=" + sum);  
  9.         }  
  10.     }  
  11.   
  12.     public void run() {  
  13.         for (int i = 0; i <= 3; i++) {  
  14.             add(100);  
  15.         }  
  16.     }  
  17. }  
/*add函数中存在同步代码块,可直接改写为同步函数,Object对象可以删除*/
[java]  view plain copy
  1. public synchronized void add(int num) {  //同步函数使用的锁是this对象
  2.     sum += num;   
  3.     System.out.println("sum=" + sum);  
  4. }  
2.同步函数设置在run函数 上,会被某个线程一直占用,直到它执行结束,其他线程才能得到处理权。所以public synchronized void run(){}即同步函数为run自己,强烈不建议这么写,否则各个子线程要排队进入run函数,变为单线程。同时不建议在run内写同步代码块。
3.同步代码块和同步函数区别
同步代码块的锁是任意的对象,同步函数的锁是this

(当线程任务只需要一个同步时完全可以使用同步函数,当线程任务需要多个同步时,必须通过锁来区分,这是必须使用同步代码块。同步代码块较为常用)


4.静态同步函数的锁
使用的锁是该方法所在类的字节码文件对象,类名.class或this.getClass()
(字节码文件对象:JVM在加载某个.class文件之后得到的一段字节??还是就是.class本身?)

public static synchronized void method(){} 静态同步函数使用的锁不是this。而是字节码文件对象。类名.class


同步函数的使用范例:

class User { 
    private String code; 
    private int cash; 

    User(String code, int cash) { 
            this.code = code; 
            this.cash = cash; 
    } 

    public String getCode() { 
            return code; 
    } 

    public void setCode(String code) { 
            this.code = code; 
    } 

    /** 
     * 业务方法 
     * @param x 添加x万元 
     */ 
    public synchronized void oper(int x) { 
            try { 
                    Thread.sleep(10L); 
                    this.cash += x; 
                    System.out.println(Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash); 
                    Thread.sleep(10L); 
            } catch (InterruptedException e) { 
                    e.printStackTrace(); 
            } 
    } 


    @Override 
    public String toString() { 
            return "User{" + 
                            "code='" + code + '\'' + 
                            ", cash=" + cash + 
                            '}'; 
    } 
}


class MyThread extends Thread { 
    private User u; 
    private int y = 0; 


    MyThread(String name, User u, int y) { 
            super(name); 
            this.u = u; 
            this.y = y; 
    } 

    public void run() { 
            u.oper(y); 
    } 



public class Demo

        public static void main(String[] args) 
        { 
                User u = new User("张三", 100); 
                MyThread t1 = new MyThread("线程A", u, 20); 
                MyThread t2 = new MyThread("线程B", u, -60); 
                MyThread t3 = new MyThread("线程C", u, -80); 
                MyThread t4 = new MyThread("线程D", u, -30); 
                MyThread t5 = new MyThread("线程E", u, 32); 
                MyThread t6 = new MyThread("线程F", u, 21); 


                t1.start(); 
                t2.start(); 
                t3.start(); 
                t4.start(); 
                t5.start(); 
                t6.start(); 
        } 

结果:

线程A运行结束,增加“20”,当前用户账户余额为:120
线程F运行结束,增加“21”,当前用户账户余额为:141
线程E运行结束,增加“32”,当前用户账户余额为:173
线程D运行结束,增加“-30”,当前用户账户余额为:143
线程C运行结束,增加“-80”,当前用户账户余额为:63
线程B运行结束,增加“-60”,当前用户账户余额为:3

如果去掉synchronized同步函数结果难以想象啊

线程D运行结束,增加“-30”,当前用户账户余额为:123
线程B运行结束,增加“-60”,当前用户账户余额为:83
线程C运行结束,增加“-80”,当前用户账户余额为:83
线程F运行结束,增加“21”,当前用户账户余额为:143
线程A运行结束,增加“20”,当前用户账户余额为:143
线程E运行结束,增加“32”,当前用户账户余额为:123


5.死锁 
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

简单演示

[java]  view plain copy
  1. package lock;  
  2.   
  3. public class Demo {  
  4.     public static void main(String[] args) {  
  5.         LockDemo d1=new LockDemo(true);  
  6.         LockDemo d2=new LockDemo(false);          
  7.         Thread t1=new Thread(d1);  
  8.         Thread t2=new Thread(d2);  
  9.         t1.start();  
  10.         t2.start();  
  11.     }  
  12. }  
  13.   
  14. class LockDemo implements Runnable  
  15. {  
  16.     private boolean flag;  
  17.     LockDemo(boolean flag){  
  18.         this.flag=flag;  
  19.     }  
             //如过要在LockDemo中定义锁对象的话一定要加static,保证锁属于这个类,各个线程共享。否则每个对象都有自己的锁,虽然名字相同。
             //static Object lock1=new Object();
             //static Object lock2=new Object();//对象不用定义在run内部否则内个线程都有两个对象。
  1.     public void run(){  
  2.         if(flag)  
  3.         {  
  4.             synchronized(Lock.lockA)  //之所以这样写是要保证多线程使用相同的锁。
  5.             { 
  6.                 System.out.println(Thread.currentThread().getName()+"if    lock1");  
  7.                 synchronized(Lock.lockB)  
  8.                 {  
  9.                     System.out.println(Thread.currentThread().getName()+"if    lock2");  
  10.                 }  
  11.             }  
  12.             System.out.println("true over");  
  13.         }  
  14.         else  
  15.         {  
  16.             synchronized(Lock.lockB)  
  17.             {  
  18.                 System.out.println(Thread.currentThread().getName()+"else  lock2");  
  19.                 synchronized(Lock.lockA)  
  20.                 {  
  21.                     System.out.println(Thread.currentThread().getName()+"else   lock1");  
  22.                 }  
  23.             }    
  24.         }  
  25.     }  
  26. }  
  27.   
  28. class Lock  
  29. {  
  30.     public static final Object lockA=new Object();  //定义为static是为了类名调用。
  31.     public static final Object lockB=new Object();  

  32.  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值