- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章一篇文章带你入门Java多线程(详细)由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
1、进程是指运行中的程序,比如我们使用qq,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间 。
2、进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程 。
1、单线程:同一个时刻,只允许执行一个线程 。
2、多线程:同一时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件 。
3、并发:同一时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发 。
4、并行:同一时刻,多个任务同时进行。多核cpu可以实现并行。并发和并行:如果开的程序太多,有可能也会触发并发 。
1、继承Thread类,重写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
30
|
//该线程每隔1秒钟。在控制台输出"喵喵",打印8次后结束线程
public
class
Thread01 {
public
static
void
main(String[] args) {
//创建一个cat对象,可以当作线程使用
Cat cat=
new
Cat();
cat.start();
//启动线程
}
}
//1、当一个类继承了 Thread 类 ,该类就可以当作线程使用
//2、我们会重写run方法,写上自己的业务代码
//3、run Thread 类 实现了 Runnable 接口的run方法
class
Cat
extends
Thread{
int
times=
0
;
@Override
public
void
run() {
//重写run方法,写上自己的业务逻辑
while
(
true
) {
System.out.println(
"喵喵"
+ ++times);
//让该线程休眠1秒钟
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
if
(times==
8
){
//设置打印次数
break
;
}
}
}
}
|
因为run()方法就是一个普通的方法,没有真正的启动一个线程,就会把run方法执行完毕,才向下执行 。
1
2
3
4
5
6
7
8
|
(
1
)
public
synchronized
void
start() {
start0();
}
//start0();是start中最主要的方法
(
2
)
//start0(); 是本地方法,是JVM调用,底层是C/C++实现
//真正实现多线程的效果,是start0(),而不是run,也可以说在start0()本地方法中去调用了Run()方法
|
2、实现Runnable接口,重写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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
public
class
Thread03 {
public
static
void
main(String[] args) {
T1 t1 =
new
T1();
T2 t2 =
new
T2();
Thread thread1=
new
Thread(t1);
Thread thread2=
new
Thread(t2);
thread1.start();
thread2.start();
}
}
class
T1
implements
Runnable{
int
count=
0
;
@Override
public
void
run() {
while
(
true
) {
//每隔1秒输出"hello,world",输出10次
System.out.println(
"hello,world "
+ ++count + Thread.currentThread().getName());
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
if
(count==
50
){
break
;
}
}
}
}
class
T2
implements
Runnable{
int
count=
0
;
@Override
public
void
run() {
while
(
true
) {
//每隔1秒输出"hello,world",输出10次
System.out.println(
"hi "
+ ++count + Thread.currentThread().getName());
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
if
(count==
60
){
break
;
}
}
}
|
1、从Java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口 。
2、实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable接口 。
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
|
class
SellTicket01
extends
Thread{
private
static
int
ticketNum=
100
;
//让多个线程共享num
@Override
public
void
run() {
while
(
true
) {
if
(ticketNum <=
0
) {
System.out.println(
"售票结束"
);
break
;
}
//休眠50毫秒
try
{
Thread.sleep(
50
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"窗口"
+ Thread.currentThread().getName() +
"售出一张票"
+
"剩余票数="
+ --ticketNum);
}
}
}
//====================main方法===========================
public
static
void
main(String[] args) {
//测试
SellTicket01 sellTicket01 =
new
SellTicket01();
SellTicket01 sellTicket02 =
new
SellTicket01();
SellTicket01 sellTicket03 =
new
SellTicket01();
//这里会出现票数超卖现象
sellTicket01.start();
sellTicket02.start();
sellTicket03.start();
}
|
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
|
class
SellTicket02
implements
Runnable{
private
int
ticketNum=
99
;
@Override
public
void
run() {
while
(
true
) {
if
(ticketNum <=
0
) {
System.out.println(
"售票结束"
);
break
;
}
//休眠50毫秒
try
{
Thread.sleep(
50
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"窗口"
+ Thread.currentThread().getName() +
"售出一张票"
+
"剩余票数="
+ --ticketNum);
}
}
}
//=================main================
public
static
void
main(String[] args) {
SellTicket02 sellTicket02 =
new
SellTicket02();
new
Thread(sellTicket02).start();
//第一个线程-窗口
new
Thread(sellTicket02).start();
//第二个线程-窗口
new
Thread(sellTicket02).start();
//第三个线程-窗口
}
|
两个方法都会有超票的现象,线程安全的问题 。
1、当线程完成任务后,会自动退出 。
2、还可以通过使用变量来控制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
ThreadExit_ {
public
static
void
main(String[] args)
throws
InterruptedException {
T t =
new
T();
t.start();
//如果希望主线程去控制t中线程的终止,需要能够控制loop
//修改loop,让t退出run方法,从而终止t线程-->通知方式
//让主线程休眠10秒,在通知t线程退出
Thread.sleep(
10000
);
t.setLoop(
false
);
//将T线程中的循环判断为false
}
}
class
T
extends
Thread{
private
int
count=
0
;
private
boolean
loop=
true
;
@Override
public
void
run() {
while
(loop){
try
{
Thread.sleep(
1000
);
//休眠50毫秒
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"T线程执行"
+ ++count);
}
}
public
void
setLoop(
boolean
loop) {
this
.loop = loop;
}
}
|
1、setName:设置线程名称,使之与参数name相同 。
2、getName:返回该线程的名称 。
3、start:该线程开始执行;java虚拟机底层调用该线程的start()方法 。
4、run:调用线程对象run方法 。
5、setPriority:更改线程的优先级 。
6、getPriority:获取线程的优先级 。
7、sleep:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行) 。
8、interrupt:中断线程 。
1、start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程 。
2、线程优先级的范围 。
3、interrupt,中断线程,但没有真正的结束线程,所以一般用于中断正在休眠线程 。
4、sleep:线程的静态方法,使当前线程休眠 。
1、yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功 。
2、join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务 。
创建一个子线程,每隔1s输出hello,输出20次,主线程每隔1s,输出hi,输出20次。要求:两个线程同时执行,当主线程输出5次后,就让子线程运行完毕,主线程再继续 。
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
|
public
class
ThreadMethod02 {
public
static
void
main(String[] args)
throws
InterruptedException {
T2 t2 =
new
T2();
t2.start();
for
(
int
i =
1
;i<=
20
;i++){
Thread.sleep(
1000
);
System.out.println(
"主线程(小弟)吃了"
+i+
"个包子"
);
if
(i==
5
){
System.out.println(
"主线程(小弟)让子线程(老大)先吃"
);
//yield 礼让
t2.yield();
//线程插队,join
// t2.join();
System.out.println(
"子线程(老大)吃完,主线程(小弟)再吃"
);
}
}
}
}
class
T2
extends
Thread{
@Override
public
void
run() {
for
(
int
i=
1
;i<=
20
;i++){
try
{
Thread.sleep(
1000
);
//休眠1秒
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"子线程(老大)吃了"
+i+
"个包子"
);
}
}
}
|
插队的话是百分百成功的,但是礼让如果资源过剩的话,礼让会不成功,例如上面资源不是特别缺乏,所以礼让会不成功 。
1、用户线程:也叫工作线程,当线程的任务执行完或通知方式结束 。
2、守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束 。
3、常见的守护线程:垃圾回收机制 。
自定义守护线程 。
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
|
public
class
ThreadMethod03 {
public
static
void
main(String[] args)
throws
InterruptedException {
MyDaemonThread myDaemonThread =
new
MyDaemonThread();
//如果我们希望当main线程结束后,子线程自动结束
//只需将子线程设为守护线程即可
myDaemonThread.setDaemon(
true
);
myDaemonThread.start();
for
(
int
i =
1
;i<=
10
;i++){
System.out.println(
"妈妈做饭"
);
Thread.sleep(
1000
);
}
}
}
class
MyDaemonThread
extends
Thread{
@Override
public
void
run() {
for
(;;){
//等价于无限循环
try
{
Thread.sleep(
50
);
//休眠50毫秒
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"我吃饭。。。"
);
}
}
}
|
MyDaemonThread类的进程会在主线程进程结束后相继结束 。
线程状态:线程可以处于一下状态之一:
NEW 。
尚未启动的线程处于此状态 。
RUNNABLE 。
在Java虚拟机中执行的线程处于此状态 。
BLOCKED 。
被阻塞等待监视器锁定的线程处于此状态 。
WAITING 。
正在等待另一个线程执行特定动作的线程处于此状态 。
TIMED_WAITING 。
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态 。
TERMINATED 。
已退出的线程处于此状态 。
RUNNABLE又可分为两个状态:Ready状态:就绪状态 和 Running运行状态 。
1、在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就是用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性.
2、也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到线程完成操作,其他线程才能对该内存地址进行操作.
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
|
public
class
SellTicket {
public
static
void
main(String[] args) {
//测试同步解决超卖现象
SellTicket03 sellTicket03 =
new
SellTicket03();
new
Thread(sellTicket03).start();
//第一个线程-窗口
new
Thread(sellTicket03).start();
//第二个线程-窗口
new
Thread(sellTicket03).start();
//第三个线程-窗口
}
}
//实现接口的方式,使用synchronized实现线程同步
class
SellTicket03
implements
Runnable{
private
boolean
loop=
true
;
private
int
ticketNum=
99
;
public
synchronized
void
sell(){
if
(ticketNum <=
0
) {
System.out.println(
"售票结束"
);
loop=
false
;
return
;
}
//休眠50毫秒
try
{
Thread.sleep(
50
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"窗口"
+ Thread.currentThread().getName() +
"售出一张票"
+
"剩余票数="
+ --ticketNum);
}
@Override
public
void
run() {
//在同一时刻只能有一个线程来执行sell方法
while
(loop) {
sell();
//sell方法是一个同步方法
}
}
}
|
synchronized关键字为锁的意思,如果有线程去调用了synchronized关键字修饰的方法,则不会去再有线程调用 。
synchronized的使用方法 。
1、Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性.
2、每个对象都对应一个可称为互斥锁的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象 。
3、关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问 。
4、同步的局限性:导致程序的执行效率要降低 。
5、同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一对象) 。
6、同步方法(静态的)的锁为当前类本身 。
同步方法静态与非静态实例 。
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
45
46
47
48
|
//实现接口的方式,使用synchronized实现线程同步
class
SellTicket03
implements
Runnable{
private
boolean
loop=
true
;
private
int
ticketNum=
99
;
Object object=
new
Object();
//同步方法(静态的)的锁为当前类本身
//1.public synchronized static void m1(){} 锁是加在SellTicket03.class 类本身
//2.如果要在静态方法中,实现一个同步代码块
//3.synchronized中的参数不能为this,要为类的class 类如:
/*public static void m2() {
synchronized (SellTicket03.class) {
System.out.println("m2");
}
}*/
public synchronized static void m1(){
}
public static void m2() {
synchronized (SellTicket03.class) {
System.out.println("m2");
}
}
//1、 public synchronized void sell(){}这是一个同步方法
//2、这时锁在this对象
//3、也可以在代码块上写synchronize , 同步代码块
public /*synchronized*/
void
sell() {
synchronized
(object) {
if
(ticketNum <=
0
) {
System.out.println(
"售票结束"
);
loop =
false
;
return
;
}
//休眠50毫秒
try
{
Thread.sleep(
50
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"窗口"
+ Thread.currentThread().getName() +
"售出一张票"
+
"剩余票数="
+ --ticketNum);
}
}
@Override
public
void
run() {
//在同一时刻只能有一个线程来执行sell方法
while
(loop) {
sell();
//sell方法是一个同步方法
}
}
}
|
1、同步方法如果没有使用static修饰:默认锁对象为this 。
2、如果方法使用static修饰,默认锁对象:当前类.class 。
3、实现的落地步骤 。
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
|
public
class
DeadLock_ {
public
static
void
main(String[] args) {
//模拟一个死锁现象
DeadLockDemo A=
new
DeadLockDemo(
true
);
DeadLockDemo B=
new
DeadLockDemo(
false
);
deadLockDemo1.start();
deadLockDemo2.start();
}
}
class
DeadLockDemo
extends
Thread{
static
Object o1 =
new
Object();
//保证多线程,共享一个对象,这里使用static
static
Object o2 =
new
Object();
boolean
flag;
public
DeadLockDemo(
boolean
flag){
//构造器
this
.flag = flag;
}
@Override
public
void
run() {
//下面的业务逻辑的分析
//1.如果flag为T , 线程就会先得到/持有 o1 对象锁 , 然后尝试去获得 o2对象锁
//2.如果线程A 得不到o2对象锁,就会Blocked
//3.如果flag为F,线程B就会先得到/持有 o2 对象锁,然后尝试去获取 o1 对象锁
//4.如果线程B 得不到 o1 对象锁,就会Blocked
if
(flag){
synchronized
(o1){
//对象互斥锁,下面就是我们同步代码
System.out.println(Thread.currentThread().getName() +
"进入1"
);
synchronized
(o2){
//这里获得li对象的监视权
System.out.println(Thread.currentThread().getName()+
"进入2"
);
}
}
}
else
{
synchronized
(o2){
System.out.println(Thread.currentThread().getName()+
"进入3"
);
synchronized
(o1){
System.out.println(Thread.currentThread().getName()+
"进入4"
);
}
}
}
}
}
|
因为线程A会去抢线程B占着的对象,线程B也会去抢线程A占着的对象,所以会出现线程锁死的现象,写代码的时候要避免这个错误 。
下面操作会释放锁 。
1、当前线程的同步方法、同步代码块执行结束 。
案例:上厕所,完事出来 。
2、当前线程在同步代码块、同步方法中遇到break、return 。
案例:没有正常的完事,经理叫你去修改bug,不得已出来 。
3、当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束 。
案例:没有正常的完事,发现忘记带纸,不得已出来 。
4、当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁.
案例:没有正常完事,觉得需要酝酿下,所以出来等会在进去 。
下面操作不会释放锁 。
1、线程执行同步代码块或同步方法时,程序调用了Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁 。
案例:上厕所,太困了,在坑位上眯了一会 。
2、线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁 。
提示:应尽量避免使用suspend()和resume()来控制线程,方法不再推荐使用 。
1、 。
(1)在main方法中启动两个线程 。
(2)第一个线程循环随机打印100以内的整数 。
(3)直到第二个线程从键盘上读取了"Q"命令 。
通过线程守护解决 。
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
|
public
class
homeWork01 {
public
static
void
main(String[] args) {
//创建线程B,并运行
B b=
new
B();
b.start();
}
}
class
A
extends
Thread{
@Override
public
void
run() {
while
(
true
){
try
{
//休眠1秒运行
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
//打印随机数
int
num = (
int
)(Math.random()*
100
);
System.out.println(num);
}
}
}
class
B
extends
Thread{
@Override
public
void
run() {
//创建A线程对象,并创建守护线程
A a=
new
A();
a.setDaemon(
true
);
a.start();
while
(
true
) {
//当输入Q的时候B线程结束,因为是守护线程,所以线程A也会跟着结束
System.out.println(
"请输入你的指令"
);
Scanner sc =
new
Scanner(System.in);
String Z = sc.next();
System.out.println(Z);
if
(Z.equals(
"Q"
)) {
System.out.println(
"B线程结束"
);
break
;
}
}
}
}
|
通过通知方式解决 。
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
45
46
47
48
49
|
public
class
homeWork01 {
public
static
void
main(String[] args) {
//创建线程A、B,并且执行线程A、B
A a=
new
A();
B b=
new
B(a);
a.start();
b.start();
}
}
class
A
extends
Thread{
private
boolean
loop=
true
;
//创建setLoop用来通知
public
void
setLoop(
boolean
loop) {
this
.loop = loop;
}
@Override
public
void
run() {
while
(loop){
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
int
num = (
int
)(Math.random()*
100
);
System.out.println(num);
}
}
}
class
B
extends
Thread{
A a;
//通过B的构造器,传入main中的线程A
public
B(A a){
this
.a=a;
}
@Override
public
void
run() {
while
(
true
) {
System.out.println(
"请输入你的指令"
);
Scanner sc =
new
Scanner(System.in);
String Z = sc.next();
System.out.println(Z);
if
(Z.equals(
"Q"
)) {
//通过setLoop提醒线程A结束
a.setLoop(
false
);
break
;
}
}
}
}
|
2、 。
(1)有两个用户分别从同一个卡上取钱(总额:10000) 。
(2)每次都取1000,当余额不足时,就不能取款了 。
(3)不能出现超取现象 —>线程同步问题 。
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
|
public
class
homeWork02 {
public
static
void
main(String[] args) {
C c=
new
C();
//将两个线程运行
new
Thread(c).start();
new
Thread(c).start();
}
}
class
C
implements
Runnable{
private
static
boolean
loop=
true
;
private
static
int
money=
10000
;
@Override
public
void
run() {
while
(loop){
//让两个线程去抢同步方法
quMoney();
}
}
public
synchronized
void
quMoney(){
if
(money<=
0
){
System.out.println(
"余额不足,线程退出"
+Thread.currentThread().getName());
loop=
false
;
return
;
}
try
{
Thread.sleep(
50
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
money=money-
1000
;
System.out.println(Thread.currentThread().getName()+
"从余额中取到了1000元还剩"
+money+
"元"
);
}
}
|
通过创建同步方法,避免超取现象 。
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
|
public
class
homeWork03 {
public
static
void
main(String[] args) {
T t=
new
T();
Thread thread=
new
Thread(t);
Thread thread1=
new
Thread(t);
thread.setName(
"t1"
);
thread1.setName(
"t2"
);
thread.start();
thread1.start();
}
}
//编写取款的线程
//因为这里涉及到多个程序共享资源,所以我们使用实现Runnable方式
class
T
implements
Runnable{
private
int
money=
10000
;
@Override
public
void
run() {
while
(
true
){
//解读
//1.这里使用 synchronized 实现了线程同步
//2.当多个线程执行到这里时,就会去争夺 this 对象锁
//3.那个线程获取到了this锁,就执行 synchronized 代码块,执行完后,会释放this对象锁
//4.获取不到this对象锁,就会blocked(阻塞),准备继续争夺
synchronized
(
this
) {
if
(money <
1000
) {
System.out.println(
"余额不足"
);
break
;
}
money -=
1000
;
System.out.println(Thread.currentThread().getName() +
"取出了1000 当前余额"
+ money);
}
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
}
|
ss T implements Runnable{ private int money=10000,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Override
public
void
run() {
while
(
true
){
//解读
//1.这里使用 synchronized 实现了线程同步
//2.当多个线程执行到这里时,就会去争夺 this 对象锁
//3.那个线程获取到了this锁,就执行 synchronized 代码块,执行完后,会释放this对象锁
//4.获取不到this对象锁,就会blocked(阻塞),准备继续争夺
synchronized
(
this
) {
if
(money <
1000
) {
System.out.println(
"余额不足"
);
break
;
}
money -=
1000
;
System.out.println(Thread.currentThread().getName() +
"取出了1000 当前余额"
+ money);
}
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
|
本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我的更多内容! 。
原文链接:https://blog.csdn.net/qq_52761400/article/details/118855374 。
最后此篇关于一篇文章带你入门Java多线程(详细)的文章就讲到这里了,如果你想了解更多关于一篇文章带你入门Java多线程(详细)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
Hive —— 入门 Hive介绍 Apache Hive是一款建立在Hadoop之上的开源数据仓库系统,可以将存储在Hadoop文件中的结构化、半结构化数据文件映射为一张数据库表,基于表提供了一
HBase —— 入门 HBase介绍 HBase是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”
零:前端目前形势 前端的发展史 HTML(5)、CSS(3)、JavaScript(ES5、ES6):编写一个个的页面 -> 给后端(PHP、Python、Go、Java) ->
在本教程中,您将了解在计算机上运行 JavaScript 的不同方法。 JavaScript 是一种流行的编程语言,具有广泛的应用程序。 JavaScript 以前主要用于使网页具有交
我曾经是一个对编程一窍不通的小白,但因为对互联网世界的好奇心和求知欲的驱使,我踏入了编程的殿堂。在学习的过程中,我发现了一门神奇的编程语言——Python。Python有着简洁、易读的语法,让初学者能
嗨,亲爱的读者们! 今天我要给大家分享一些关于Python爬虫的小案例。你是否曾为了获取特定网页上的数据而烦恼过?或者是否好奇如何从网页中提取信息以供自己使用?那么,这篇文章将会给你一些启示和灵感。
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 8 年前。 Improv
我想创建一个像https://apprtc.appspot.com/?r=04188292这样的应用程序。我对 webrtc 了解一点,但无法掌握 google app-engine。如何为 java
我刚刚开始使用 Python 并编写了一个简单的周边程序。但是,每当我在终端中键入 python perimeter.py 时,都会收到以下错误,我不知道如何解决。 >>> python perime
Redis有5个基本数据结构,string、list、hash、set和zset。它们是日常开发中使用频率非常高应用最为广泛的数据结构,把这5个数据结构都吃透了,你就掌握了Redis应用知识的一半了
创建发布web项目 具体步骤: 1.在开发工具中创建一个dynamic web project helloword 2.在webContent中创建index.html文件 3.发布web应用到
如果你在 Ubuntu 上使用终端的时间很长,你可能会希望调整终端的字体和大小以获取一种良好的体验。 更改字体是一种最简单但最直观的 Linux 的终端自定义 的方法。让我
1. 前言 ADODB 是 Active Data Objects Data Base 的简称,它是一种 PHP 存取数据库的函式组件。现在 SFS3 系统 (校园自由软件交流网学务系统) 计划的
我对 neo4j 完全陌生,我很抱歉提出这样一个基本问题。我已经安装了neo4j,我正在使用shell“localhost:7474/webadmin/#/console/” 我正在寻找一个很好的例子
我正在阅读 ios 4 的核心音频,目的是构建一个小测试应用程序。 在这一点上,我对所有 api 的研究感到非常困惑。理想情况下,我想知道如何从两个 mp3 中提取一些样本到数组中。 然后在回调循环中
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是无关紧要的,因
我下载了 GNUStep并安装了它,但是我不确定在哪里可以找到 IDE。有谁知道什么程序可以用作 GNUStep IDE/从哪里获取它们?否则,有没有人知道有关如何创建和编译基本 GNUStep 程序
我正在尝试开始使用 Apache Solr,但有些事情我不清楚。通读tutorial ,我已经设置了一个正在运行的 Solr 实例。我感到困惑的是 Solr 的所有配置(架构等)都是 XML 格式的。
请问有没有关于如何开始使用 BruTile 的文档? 我目前正在使用 SharpMap,我需要预缓存切片以加快进程 最佳答案 我今天正在研究这个:)Mapsui项目site严重依赖 SharpMap
尽我所能,我无法让 CEDET 做任何事情。 Emacs 24.3。我下载了最新的 CEDET 快照。我从他的底部(不是这样)Gentle Introduction 中获取了 Alex Ott 的设置
我是一名优秀的程序员,十分优秀!