Java 多线程入门
进程与线程
在学习Java多线程之前,先简单复习一下进程与线程的知识。
进程:进程是系统进行资源分配和调度的基本单位,可以将进程理解为一个正在执行的程序,比如一款游戏。
线程:线程是程序执行的最小单位,一个进程可由一个或多个线程组成,在一款运行的游戏中通常会有界面
更新线程、游戏逻辑线程等,线程切换的开销远小于进程切换的开销。
图1
在图1中,蓝色框表示进程,黄色框表示线程。进程拥有代码、数据等资源,这些资源是共享的,3个线程都可
以访问,同时每个线程又拥有私有的栈空间。
Java线程状态图
线程的五种状态:
1)新建状态(New):线程对象实例化后就进入了新建状态。
2)就绪状态(Runnable):线程对象实例化后,其他线程调用了该对象的start()方法,虚拟机便会启
动该线程,处于就绪状态的线程随时可能被调度执行。
3)运行状态(Running):线程获得了时间片,开始执行。只能从就绪状态进入运行状态。
4)阻塞状态(Blocked):线程因为某个原因暂停执行,并让出CPU的使用权后便进入了阻塞状态。
等待阻塞:调用运行线程的wait()方法,虚拟机会把该线程放入等待池。
同步阻塞:运行线程获取对象的同步锁时,该锁已被其他线程获得,虚拟机会把该线程放入锁定池。
其他线程:调用运行线程的sleep()方法或join()方法,或线程发出I/O请求时,进入阻塞状态。
5)结束状态(Dead):线程正常执行完或异常退出时,进入了结束状态。
Java线程实现
Java语言提供了两种实现线程的方式:
1)通过继承Thread类实现线程
public class ThreadTest {
public static void main(String[] args){
Thread thread = new MyThread(); //创建线程
thread.start(); //启动线程
}
}
//继承Thread类
class MyThread extends Thread{
@Override
public void run() {
int count = 7;
while(count>0){
System.out.println(count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
}
}
}
2)通过实现Runnable接口实现线程
public class ThreadTest {
public static void main(String[] args){
Runnable runnable = new MyThread();
//将Runnable对象传递给Thread构造器
Thread thread = new Thread(runnable);
thread.start();
}
}
//实现了Runnable接口
class MyThread implements Runnable{
@Override
public void run() {
int count = 7;
while(count>0){
System.out.println(count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
}
}
}
两种方式都覆写了run()方法,run()方法内定义了线程的执行内容,我们只能通过线程的start()方法来
启动线程,且start()方法只能调用一次,当线程进入执行状态时,虚拟机会回调线程的run()方法。直
接调用线程的run()方法,并不会启动线程,只会像普通方法一样去执行。
其实,Thread类本身也实现了Runnable接口。这两种方式都可以实现线程,但Java语言只支持单继
承,如果扩展了Thread类就无法再扩展其他类,远没有实现接口灵活。
线程常用方法
1)Thread类
Thread():用于构造一个新的Thread。
Thread(Runnable target):用于构造一个新的Thread,该线程使用了指定target的run方法。
Thread(ThreadGroup group,Runnable target):用于在指定的线程组中构造一个新的Thread,该
线程使用了指定target的run方法。
currentThread():获得当前运行线程的对象引用。
interrupt():将当前线程置为中断状态。
sleep(long millis):使当前运行的线程进入睡眠状态,睡眠时间至少为指定毫秒数。
join():等待这个线程结束,即在一个线程中调用other.join(),将等待other线程结束后才继续本线程。
yield():当前执行的线程让出CPU的使用权,从运行状态进入就绪状态,让其他就绪线程执行。
2)Object类
wait():让当前线程进入等待阻塞状态,直到其他线程调用了此对象的notify()或notifyAll()方法后,当
前线程才被唤醒进入就绪状态。
notify():唤醒在此对象监控器上等待的单个线程。
notifyAll():唤醒在此对象监控器上等待的所以线程。
注:wait()、notify()、notifyAll()都依赖于同步锁,而同步锁是对象持有的,且每个对象只有一个,所以
这些方法定义在Object类中,而不是Thread类中。
3)yield()、sleep()、wait()比较
wait():让线程从运行状态进入等待阻塞状态,并且会释放它所持有的同步锁。
yield():让线程从运行状态进入就绪状态,不会释放它锁持有的同步锁。
sleep():让线程从运行状态进入阻塞状态,不会释放它锁持有的同步锁。
线程同步
先看一个多线程模拟卖票的例子,总票数7张,两个线程同时卖票:
public class ThreadTest{
public static void main(String[] args){
Runnable r = new MyThread();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class MyThread implements Runnable{
private int tickets = 7; //票数
@Override
public void run(){
while(tickets>0){
System.out.println("tickets:"+tickets);
tickets--;
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
运行结果:
运行结果不符合我们的预期,因为两个线程使用共享变量tickets,存在着由于交叉操作而破坏数据的可能性,
这种潜在的干扰被称作临界区,通过同步对临界区的访问可以避免这种干扰。
在Java语言中,每个对象都有与之关联的同步锁,并且可以通过使用synchronized方法或语句来获取或释放
这个锁。在多线程协作时,如果涉及到对共享对象的访问,在访问对象之前,线程必须获取到该对象的同步
锁,获取到同步锁后可以阻止其他线程获得这个锁,直到持有锁的线程释放掉锁为止。
public class ThreadTest{
public static void main(String[] args){
Runnable r = new MyThread();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class MyThread implements Runnable{
private int tickets = 7;
@Override
public void run(){
while(tickets>0){
synchronized(this){ //获取当前对象的同步锁
if(tickets>0){
System.out.println("tickets:"+tickets);
tickets--;
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
}
}
运行结果:
synchronized用法:
1)synchronized方法:如果一个线程要在某个对象上调用synchronized方法,那么它必须先获取这个对象的
锁,然后执行方法体,最后释放这个对象上的锁,而与此同时,在同一个对象上调用synchronized方法的其他
线程将阻塞,直到这个对象的锁被释放为止。
public synchronized void show(){
System.out.println("hello world");
}
2)synchronized静态方法:静态方法也可以被声明为synchronized的,每个类都有与之相关联的Class对象,
而静态同步方法获取的就是它所属类的Class对象上的锁,两个线程不能同时执行同一个类的静态同步方法,如
果静态数据是在线程之间共享的,那么对它的访问就必须利用静态同步方法来进行保护。
3)synchronized语句:静态语句可以使我们获取任何对象上的锁而不仅仅是当前对象上的锁,也能够让我们定
义比方法还要小的同步代码区,这样可以让线程持有锁的时间尽可能短,从而提高性能。
private final Object lock = new Object();
public void show(){
synchronized(lock){
System.out.println("hello world");
}
}
Java 多线程入门的更多相关文章
- java多线程入门学习(一)
java多线程入门学习(一) 一.java多线程之前 进程:每一个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销.一个进程包括1--n个线程. 线程:同一类线程共享代码 ...
- Java多线程学习(一)Java多线程入门
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79640870 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
- (转载)Java多线程入门理解
转载出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更 ...
- java多线程入门
一.认识多任务.多进程.单线程.多线程 要认识多线程就要从操作系统的原理说起. 以前古老的DOS操作系统(V 6.22)是单任务的,还没有线程的概念,系统在每次只能做一件事情.比如你在copy东西 ...
- Java多线程入门知识点梳理
前言 在多核时代,高并发时代,对系统并行处理能力有很高要求.多线程就是这个时代最好的产物.通过使用多线程可以增强系统并行处理能力,提高CPU资源的有效利用:从而提高系统的处理能力.常见应用场景如:多窗 ...
- Java多线程入门及实战
基本概念: 1: 程序 2 进程 3 线程 4 进程和线程的区别 5 进程和程序的区别 Java实现多线程的方法: 1 继承Thread 2 实现Runable 3 实现callable 4 线程池的 ...
- Java多线程入门中几个常用的方法
一.currentThread()方法 currentThread方法就是返回当前被调用的线程. 该方法为一个本地方法,原码如下: /** * Returns a reference to the c ...
- Java多线程入门Ⅱ
线程的让步 线程让出自己占用的CPU资源 线程让出资源,不指定让给谁 线程让出资源,指定让给谁 方法1: public static void yield(); 线程实现交替打印 import jav ...
- Java多线程学习(四)等待/通知(wait/notify)机制
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79690279 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
随机推荐
- Eclipse设置代码自动提示
Eclipse只需几步简单的设置就可以像idea那样代码自动提示了,喜欢的小伙伴可以赶紧动手设置,提升效率. 第一步:打开Eclipse --> Window --> Preference ...
- Java自学能学会吗?最新Java高级学习路线
就好像一千个人心中有一千个哈利波特一样,这个答案不绝对也不唯一,Java好学但自学的确阻力有些大,作为一门技术语言,它所蕴含的智慧绝对不是靠几本XX入门,XX框架之类的书所能概括,自学Java你要做好 ...
- TCP/IP详解 卷1 第二十一章 TCP的超时与重传
21.1 引言 可靠性的保证之一就是超时重传 前面两个超时重传的例子 1) ICMP端口不能到达时,TFTP客户使用UDP实现了一个简单的超时和重传机制,假定5s是一个适当是时间间隔,并每隔5s进行 ...
- 看图说话,P2P 分享率 90% 以上的 P2P-CDN 服务,来了!
事情是这样的:今年年初的时候,公司准备筹划一个直播项目,在原有的 APP 中嵌入直播模块,其中的一个问题就是直播加速服务的选取. 老板让我负责直播加速的产品选型,那天老板把我叫到办公室,语重心长地说: ...
- Tomcat时区设置方法[转]
最近被问到了关于Tomcat时区设置方法,还没碰到类似的问题,网上说是记录日志和定时任务时可能会出现,目前还未发现,先记录下来. 原文地址:http://blog.csdn.net/x_yp/arti ...
- Java设计模式——模板方法模式
转载自:https://www.cnblogs.com/zplogo/p/6428593.html 用抽象基类定义算法框架 RefreshBeverage package com.pattern.te ...
- [Sdoi2017]树点涂色 [lct 线段树]
[Sdoi2017]树点涂色 题意:一棵有根树,支持x到根染成新颜色,求x到y颜色数,求x子树里点到根颜色数最大值 考场发现这个信息是可减的,但是没想到lct 特意设计成lct的形式! 如何求颜色数? ...
- POJ 2409 Let it Bead [置换群 Polya]
传送门 题意:$m$种颜色$n$颗珠子,定义旋转和翻转两种置换,求不等价着色数 暴力求每个置换的循环节也许会$T?$ 我们可以发现一些规律: 翻转: $n$为奇数时每个置换有$1+\frac{n-1} ...
- BZOJ 2194 [快速傅里叶变换 卷积]
题意:请计算C[k]=sigma(a[i]*b[i-k]) 其中 k < = i < n ,并且有 n < = 10 ^ 5. a,b中的元素均为小于等于100的非负整数. 卷积 ( ...
- Quartz.NET 3.0 正式发布
Quartz.NET是一个强大.开源.轻量的作业调度框架,你能够用它来为执行一个作业而创建简单的或复杂的作业调度.它有很多特征,如:数据库支持,集群,插件,支持cron-like表达式等等.在2017 ...