- 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的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我将 Bootstrap 与 css 和 java 脚本结合使用。在不影响前端代码的情况下,我真的很难在css中绘制这个背景。在许多问题中,人们将宽度和高度设置为 0%。但是由于我的导航栏,我不能使用
我正在用 c 编写一个程序来读取文件的内容。代码如下: #include void main() { char line[90]; while(scanf("%79[^\
我想使用 javascript 获取矩阵数组的所有对 Angular 线。假设输入输出如下: input = [ [1,2,3], [4,5,6], [7,8,9], ] output =
可以用pdfmake绘制lines,circles和other shapes吗?如果是,是否有documentation或样本?我想用jsPDF替换pdfmake。 最佳答案 是的,有可能。 pdfm
我有一个小svg小部件,其目的是显示角度列表(参见图片)。 现在,角度是线元素,仅具有笔触,没有填充。但是现在我想使用一种“内部填充”颜色和一种“笔触/边框”颜色。我猜想line元素不能解决这个问题,
我正在为带有三角对象的 3D 场景编写一个非常基本的光线转换器,一切都工作正常,直到我决定尝试从场景原点 (0/0/0) 以外的点转换光线。 但是,当我将光线原点更改为 (0/1/0) 时,相交测试突
这个问题已经有答案了: Why do people write "#!/usr/bin/env python" on the first line of a Python script? (22 个回
如何使用大约 50 个星号 * 并使用 for 循环绘制一条水平线?当我尝试这样做时,结果是垂直(而不是水平)列出 50 个星号。 public void drawAstline() { f
这是一个让球以对角线方式下降的 UI,但球保持静止;线程似乎无法正常工作。你能告诉我如何让球移动吗? 请下载一个球并更改目录,以便程序可以找到您的球的分配位置。没有必要下载足球场,但如果您愿意,也可以
我在我的一个项目中使用 Jmeter 和 Ant,当我们生成报告时,它会在报告中显示 URL、#Samples、失败、成功率、平均时间、最短时间、最长时间。 我也想在报告中包含 90% 的时间线。 现
我有一个不寻常的问题,希望有人能帮助我。我想用 Canvas (android) 画一条 Swing 或波浪线,但我不知道该怎么做。它将成为蝌蚪的尾部,所以理想情况下我希望它的形状更像三角形,一端更大
这个问题已经有答案了: Checking Collision of Shapes with JavaFX (1 个回答) 已关闭 8 年前。 我正在使用 JavaFx 8 库。 我的任务很简单:我想检
如何按编号的百分比拆分文件。行数? 假设我想将我的文件分成 3 个部分(60%/20%/20% 部分),我可以手动执行此操作,-_-: $ wc -l brown.txt 57339 brown.tx
我正在努力实现这样的目标: 但这就是我设法做到的。 你能帮我实现预期的结果吗? 更新: 如果我删除 bootstrap.css 依赖项,问题就会消失。我怎样才能让它与 Bootstrap 一起工作?
我目前正在构建一个网站,但遇到了 transform: scale 的问题。我有一个按钮,当用户将鼠标悬停在它上面时,会发生两件事: 背景以对 Angular 线“扫过” 按钮标签颜色改变 按钮稍微变
我需要使用直线和仿射变换绘制大量数据点的图形(缩放图形以适合 View )。 目前,我正在使用 NSBezierPath,但我认为它效率很低(因为点在绘制之前被复制到贝塞尔路径)。通过将我的数据切割成
我正在使用基于 SVM 分类的 HOG 特征检测器。我可以成功提取车牌,但提取的车牌除了车牌号外还有一些不必要的像素/线。我的图像处理流程如下: 在灰度图像上应用 HOG 检测器 裁剪检测到的区域 调
我有以下图片: 我想填充它的轮廓(即我想在这张图片中填充线条)。 我尝试了形态学闭合,但使用大小为 3x3 的矩形内核和 10 迭代并没有填满整个边界。我还尝试了一个 21x21 内核和 1 迭代,但
我必须找到一种算法,可以找到两组数组之间的交集总数,而其中一个数组已排序。 举个例子,我们有这两个数组,我们向相应的数字画直线。 这两个数组为我们提供了总共 7 个交集。 有什么样的算法可以帮助我解决
简单地说 - 我想使用透视投影从近裁剪平面绘制一条射线/线到远裁剪平面。我有我认为是使用各种 OpenGL/图形编程指南中描述的方法通过单击鼠标生成的正确标准化的世界坐标。 我遇到的问题是我的光线似乎
我是一名优秀的程序员,十分优秀!