java多线程之基础
1.Java中有3种创建多线程的方法:
第一种继承Thread类:Thread类是在java.lang包中定义的。
第二种实现Runnable接口:在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。
第三种实现Callable和Future接口:接口的call()方法具有返回值。
三种方法使用区别:
1.实现Runnable接口可以避免Java类单继承的限制。
2.当一组线程访问相同资源的时候,适合使用Runnable接口。
3.如果需要返回值,可以使用Callable和Future。
2.Java线程的五种状态
新建状态:线程对象被创建后,即是新建状态。如:Thread t = new MyThread();
就绪状态:创建的线程对象调用start()方法后,进入就绪状态。就绪状态的线程并不是已经执行,只是能够被CPU调用。
运行状态:CPU调用处于就绪状态的线程,该线程才能真正被执行。就绪状态是进入运行状态的唯一入口,也就是说CPU只能调用就绪状态的线程。
阻塞状态:处于运行状态的线程由于某种原因放弃对CPU的使用权,此时进入阻塞状态。直到再次进入就绪状态,才有可能被CPU调用。
根据阻塞产生的原因不同,阻塞状态可分为三种:
1.等待阻塞:运行状态中的线程执行了wai()方法,进入等待阻塞状态。
2.同步阻塞:线程在获得同步锁(synchronized)失败,进入同步阻塞状态。
3.其他阻塞:线程调用sleep(),join()或者发出I/O请求的时候,线程会进入阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态:线程运行完了或者因为异常退出run()方法,表示该线程已经结束。
3.多线程示例
说明1:我们知道启动线程使用的是Thread类的start()方法,但start()方法最后仍然会自动调用run()方法,那为什么不直接使用run()方法呢?因为Java是没有权限去开启线程、操作硬件的。
它通过调用底层的C++代码来开启一个新的线程。查看start()方法源码,我们会发现它调用了一个native修饰的start0()方法,
所以当我们调用run()方法时线程并没有开启,我们只是单纯地通过对象去调用一个普通方法而已,此时执行的线程还是主线程
说明2:程序由于3个Thread对象共同执行一个Runnable对象中的代码,因此可能会造成线程的不安全,比如可能ticket会输出-1(如果我们System.out....语句前加上线程休眠操作,该情况将很有可能出现),
这种情况的出现是由于,一个线程在判断ticket为1>0后,还没有来得及减1,另一个线程已经将ticket减1,变为了0,那么接下来之前的线程再将ticket减1,便得到了-1。
这就需要加入同步操作(即互斥锁),确保同一时刻只有一个线程在执行每次for循环中的操作。
说明3:synchronized同步锁:首先同步加锁的是对象而不是代码,所以锁只对同一个对象有作用。synchronized关键字是不能继承的,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){}。
synchronized它包括两种用法synchronized方法和 synchronized块。如果Object lock = “lock”改成 Object lock = new Object() 同步锁是无效的,不会起到同步作用。
说明4:一般运行系统中,如果较高优先级的线程没有调用 sleep 方法,又没有受到 I\O 阻塞。那么较低优先级线程只能等待所有较高优先级的线程运行结束才有机会运行。
4.synchronized关键字
1.synchronized 关键字加到static静态方法和synchronized(class)代码块上都是是给Class类上锁。
2.synchronized 关键字加到实例方法上是给对象实例上锁。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律