0%

Java-线程学习笔记(下)

线程学习笔记(下)

同步

  • 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问
  • 由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法synchronized块

同步:也称为并发,由于有多个线程访问同一份资源,所以确保资源安全 -->线程安全 >eg:HashTable是线程安全的,而HashMap是线程不安全的
>eg:StringBuffer是线程安全的,而StringBuild是线程不安全的

一、 同步块

synchronized(引用类型|this|类.class){}

二、 同步方法 > public synchronized void test(){}

而同步块中的类.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
/**
* 〈单例设计模式:确保一个类只有一个对象〉
* 确保一个类只有一个对象
* 懒汉式
* 1、构造器私有化,避免外部直接创建对象
* 2、声明一个私有的静态变量
* 3、创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
* @author Zephon
*/
class Jvm{
//声明一个私有的静态变量
private static Jvm instance = null;

//构造器私有化,避免外部直接创建对象
private Jvm(){

}
//创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
public static Jvm getInstance(long time){
if (null == instance) {
try {
Thread.sleep(time);//延时
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Jvm();
}
return instance;
}
}

死锁

过多的同步容易造成死锁

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

/**
* 死锁案例
* @author Zephon
*/
public class SynDemo03 {
public static void main(String[] args){
Object goods = new Object();
Object money = new Object();
Test t1 = new Test(goods,money);
Test2 t2 = new Test2(goods,money);
Thread proxy = new Thread(t1);
Thread proxy2 = new Thread(t2);
proxy.start();
proxy2.start();
}
}

class Test implements Runnable{
Object goods;
Object money;

public Test(Object goods, Object money) {
this.goods = goods;
this.money = money;
}

@Override
public void run() {
while(true){
test();
}
}
public void test(){
synchronized (goods){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (money){

}
}

System.out.println("一手给钱");
}

}

class Test2 implements Runnable{
Object goods ;
Object money ;

public Test2(Object goods, Object money) {
this.goods = goods;
this.money = money;
}

@Override
public void run() {
while(true){
test();
}
}
public void test(){
synchronized (money){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (goods){

}
}

System.out.println("一手给货");
}
}

生产者消费者模式(不是设计模式而是解决死锁的一种方式)

  • 生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程-即所谓的“生产者”和“消费者”-在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗说这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
  • 要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常常用的方法有信号灯法、管程等。如果解决方法不够完善,则容易出现死锁情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。
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
/**
* 〈一句话功能简述〉<br>
* 〈生产者消费者模式 信号灯法〉
* wait();等待,释放锁 而sleep不释放锁
* notify()/notifyAll:唤醒
* @author Zephon
*/
public class Movie {
private String pic ;
//信号灯
//flag-->T 生产者生产,消费者等待,生产完后,通知消费
//flag-->F 消费者消费,生产者等待,消费完后,通知生产
private boolean flag = true;
public synchronized void play(String pic){
if(!flag){//生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//开始生产
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.pic = pic;
//通知消费
this.notify();
//生产者停下
this.flag = false;
}
public synchronized void watch(){
if(flag){//消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(pic);
this.notifyAll();
this.flag = true;
}

public static void main(String[] args){
//共同资源
Movie m = new Movie();

//多线程
Player p = new Player(m);
Watcher w = new Watcher(m);

new Thread(p).start();
new Thread(w).start();
}
}

/**
* 生产者
*/
class Player implements Runnable{
private Movie m;

public Player(Movie m) {
this.m = m;
}

@Override
public void run() {
for(int i=0;i<20;i++){
if(i%2==0){
m.play("偶数");
}else{
m.play("奇数");
}
}
}
}
class Watcher implements Runnable{
private Movie m;

public Watcher(Movie m) {
this.m = m;
}

@Override
public void run() {
for(int i=0;i<20;i++){
m.watch();
}
}
}

任务调度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 任务调度
* 了解即可
* @author Zephon
*/
public class TimeDemo01 {
public static void main(String[] args){
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello world");
}
},new Date(System.currentTimeMillis()+1000),200);
}
}