tonglin0325的个人主页

Java同步synchronized与死锁

多个线程要操作同一资源时就有可能出现资源的同步问题

同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行。

解决资源共享的同步操作,可以使用同步代码块同步方法两种方式完成。

 

<1>同步代码块

所谓代码块就是指使用“{}”括起来的一段代码,根据其位置和声明的不同,可以分为普通代码块、构造块、静态块3种,如果在代码块上加上synchronized关键字,则此代码块就称为同步代码块。

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
package java_thread;

class MyThread_2 implements Runnable{
private int ticket = 5;

@Override
public void run() { //覆写Thread类中的run()方法
// TODO 自动生成的方法存根
for (int i=0;i<10;i++){
synchronized (this) { //设置需要同步的操作
if(ticket>0){
try{
Thread.sleep(300);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("卖票:ticket="+ticket--);
}
}
// this.sale(); //调用同步方法
}
}

}

public class Runnable_demo2 {

public static void main(String[] args) {
// TODO 自动生成的方法存根
MyThread_2 mt = new MyThread_2(); //实例化Runnable子类对象
Thread t1 = new Thread(mt); //实例化Thread类对象
Thread t2 = new Thread(mt); //实例化Thread类对象
Thread t3 = new Thread(mt); //实例化Thread类对象
t1.start(); //启动线程
t2.start(); //启动线程
t3.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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package java_thread;

class Output{
public void output(String name){
int len = name.length();
synchronized(this){ //不能使用name,因为&ldquo;输出1&rdquo;和"输出2"两个字符串不是同一个对象
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}


public class Huchi {

private void init(){
final Output outputer = new Output();
//线程1
new Thread(new Runnable(){
@Override
public void run() { //覆写Thread类中的run()方法
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//new Output().output("输出1"); //也不能new对象,new对象的话,this就不代表同一个对象了
outputer.output("输出1");
}
}
}).start();

//线程2
new Thread(new Runnable(){
@Override
public void run() { //覆写Thread类中的run()方法
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//new Output().output("输出1"); //也不能new对象,new对象的话,this就不代表同一个对象了
outputer.output("输出2");
}
}
}).start();

}

public static void main(String[] args) {
// TODO 自动生成的方法存根
new Huchi().init();
}

}

 

 

<2>同步方法

也可以使用synchronized关键字将一个方法声明成同步方法

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
class MyThread_2 implements Runnable{
private int ticket = 5;

@Override
public void run() { //覆写Thread类中的run()方法
// TODO 自动生成的方法存根
for (int i=0;i<10;i++){
this.sale(); //调用同步方法
}
}

public synchronized void sale(){ //声明同步方法
if(ticket>0){
try{
Thread.sleep(300);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("卖票:ticket="+ticket--);
}
}

}

public class Runnable_demo2 {

public static void main(String[] args) {
// TODO 自动生成的方法存根
MyThread_2 mt = new MyThread_2(); //实例化Runnable子类对象
Thread t1 = new Thread(mt); //实例化Thread类对象
Thread t2 = new Thread(mt); //实例化Thread类对象
Thread t3 = new Thread(mt); //实例化Thread类对象
t1.start(); //启动线程
t2.start(); //启动线程
t3.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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
class Info{
private String name = "张三";
private String content = "学生";
private boolean flag = false;

public synchronized void set(String name,String content){ //设置信息名称及内容
if(!flag){ //标志位为false,不可以生产,在这里等待取走
try{
super.wait(); //等待消费者取走
}catch(InterruptedException e){
e.printStackTrace();
}
}
this.setName(name); //设置信息名称
try{
Thread.sleep(300); //加入延迟
}catch(InterruptedException e){
e.printStackTrace();
}
this.setContent(content); //设置信息内容
flag = false; //标志位为true,表示可以取走
super.notify(); //唤醒等待线程
}

public synchronized void get(){ //取得信息内容
if(flag){ //标志位为true,不可以取走
try{
super.wait(); //等待生产者生产
}catch(InterruptedException e){
e.printStackTrace();
}
}
try {
Thread.sleep(300); //加入延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+"-->"+this.getContent()); //输出信息
flag = true; //修改标志位为true,表示可以生产
super.notify(); //唤醒等待线程
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

}

class Producer implements Runnable{ //定义生产者线程

private Info info = null; //保存Info引用

public Producer(Info info) { //构造函数
super();
this.info = info;
}

@Override
public void run() {
// TODO 自动生成的方法存根
boolean flag = false;
for(int i=0;i<50;i++){ //50次反复修改name和content的值
if(flag){
this.info.set("张三", "学生");
flag = false;
}else{
this.info.set("李四", "老师");
flag = true;
}
}
}

}

class Consumer implements Runnable{ //定义生产者线程

private Info info = null; //保存Info引用

public Consumer(Info info) { //构造函数
super();
this.info = info;
}

@Override
public void run() {
// TODO 自动生成的方法存根
for(int i=0;i<50;i++){ //50次反复修改name和content的值
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info.get();
}
}

}

public class ThreadInfo_demo {

public static void main(String[] args) {
// TODO 自动生成的方法存根
Info i = new Info();
Producer pro = new Producer(i);
Consumer con = new Consumer(i);
new Thread(pro).start();
new Thread(con).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
27
28
29
30
31
32
33
class MYThread implements Runnable{

private boolean flag = true; //定义标志位属性

@Override
public void run() {
// TODO 自动生成的方法存根
int i = 0;
while(this.flag){ //循环输出
while(true){
System.out.println(Thread.currentThread().getName()+(i++)); //输出当前线程名称
}
}
}

public void stop(){ //编写停止方法
this.flag = false; //修改标志位
}

}

public class ThreadStop_demo {

public static void main(String[] args) {
// TODO 自动生成的方法存根
MYThread my = new MYThread();
Thread t = new Thread(my,"线程");
t.start();
my.stop();
}

}

 

互斥性

output1和output2两段代码互斥,检查的都是outputer这个对象

 

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package java_thread;

class Output{
public void output(String name){
int len = name.length();
synchronized(this){ //不能使用name,因为&ldquo;输出1&rdquo;和"输出2"两个字符串不是同一个对象
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}

public synchronized void output2(String name){ //output1和output2两段代码互斥,检查的都是outputer这个对象
int len = name.length();
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}


public class Huchi {

private void init(){
final Output outputer = new Output();
//线程1
new Thread(new Runnable(){
@Override
public void run() { //覆写Thread类中的run()方法
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//new Output().output("输出1"); //也不能new对象,new对象的话,this就不代表同一个对象了
outputer.output("输出1");
}
}
}).start();

//线程2
new Thread(new Runnable(){
@Override
public void run() { //覆写Thread类中的run()方法
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//new Output().output("输出1"); //也不能new对象,new对象的话,this就不代表同一个对象了
outputer.output2("输出2");
}
}
}).start();

}

public static void main(String[] args) {
// TODO 自动生成的方法存根
new Huchi().init();
}

}

 

static静态方法调用的对象是字节码对象,所以要把output1的this改成Output.class

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package java_thread;

class Output{
public void output1(String name){
int len = name.length();
synchronized(this){ //不能使用name,因为&ldquo;输出1&rdquo;和"输出2"两个字符串不是同一个对象
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}

//static静态方法调用的对象是字节码对象,所以要把output1的this改成Output.class
public static synchronized void output3(String name){ //output1和output3不同步,除非把output1的this改成Output.class
int len = name.length();
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}

}


public class Huchi {

private void init(){
final Output outputer = new Output();
//线程1
new Thread(new Runnable(){
@Override
public void run() { //覆写Thread类中的run()方法
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//new Output().output("输出1"); //也不能new对象,new对象的话,this就不代表同一个对象了
outputer.output1("输出1");
}
}
}).start();

//线程2
new Thread(new Runnable(){
@Override
public void run() { //覆写Thread类中的run()方法
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//new Output().output("输出1"); //也不能new对象,new对象的话,this就不代表同一个对象了
outputer.output3("输出2");
}
}
}).start();

}

public static void main(String[] args) {
// TODO 自动生成的方法存根
new Huchi().init();
}

}

 this      Output.class

    

 

面试题:子线程循环 10 次,接着主线程循环 100,接着又回到子线程循环 10 次,接着再回到主线程又循环 100,如此循环50 次,请写出程序

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package java_thread;

import java.util.concurrent.atomic.AtomicInteger;
//张孝祥java面试题28
//子线程循环 10 次,接着主线程循环 100,接着又回到子线程循环 10 次,接着再回到主线程又循环 100,如此循环50 次,请写出程序

public class TraditionalThreadCommunication {

/**
* @param args
*/
public static void main(String[] args) {
final Business business = new Business();
new Thread(
new Runnable() {
@Override
public void run() {
for(int i=1;i<=50;i++){
business.sub(i);
}
}
}
).start();

for(int i=1;i<=50;i++){
business.main(i);
}

}
}

class Business {
private boolean bShouldSub = true;
public synchronized void sub(int i){
while(!bShouldSub){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int j=1;j<=10;j++){
System.out.println("sub thread sequence of " + j + ",loop of " + i);
}
bShouldSub = false;
this.notify();
}

public synchronized void main(int i){
while(bShouldSub){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int j=1;j<=100;j++){
System.out.println("main thread sequence of " + j + ",loop of " + i);
}
bShouldSub = true;
this.notify();
}
}