Java多线程之线程安全二


线程的同步

线程的安全问题:当多个线程访问共享数据的时候,就有可能出现线程安全的问题。

产生的原因:如果当一个线程正在访问一个数据的时候,另一个线程也参与了进来,那么就会出现线程安全问题。

解决思路:我们可以将操作共享数据的代码封装起来,当有一个线程正在执行这部分代码的时候,其他线程不能参加执行

在Java中可以通过同步代码块和同步方法来实现这个操作

语法:

1.synchronized(对象){//同步锁,通常会使用this作为同步锁

//被同步的代码块

}

2.同步方法:直接用synchronized修饰方法

同步方法使用的同步锁是默认的

1)非静态方法使用的同步锁是当前类的对象(this)

2)静态方法使用的同步锁是当前类的Class对象(类名.class)

问:同步处理能够有效的解决多线程的数据安全问题,那么是不是所有的方法或代码块都进行同步处理?

答:不能这么做,因为使用同步处理虽然能够解决多线程的数据安全问题,但是同时会降低代码的执行效率

问:在什么情况下需要使用同步?

答:当多线程操作共享数据的时候,需要进行同步处理

关键词:多线程、共享数据

同步的前提:多个线程必须使用同一把锁

注意事项:不要乱用同步处理

【1:

public classListThread extends Thread {

private Vector list;

public ListThread(Vectorlist){

this.list = list;

}

public void run(){

for(int i=1;i<=10000;i++){

list.add(i);

}

}

}

public static void main(String[] args){

Vector list =new Vector(); //共享资源

ListThread t1 = newListThread(list);         //构造线程对象,传入list

ListThread t2 = newListThread(list);         //t1和t2共享一个list

t1.start();

t2.start();

try {

t1.join();

t2.join();//等待子线程执行结束

} catch (InterruptedExceptione) {

// TODOAuto-generated catch block

e.printStackTrace();

}

System.out.println(list.size());

}

2:

线程安全问题多个线程对同一资源进行争用时产生的问题(关于线程同步问题)

异步多线程,你走你的,我走我的

同步多线程执行到某一环节,要进行协调,让某一线程先执行,另一线程后执行,避免对同一资源的争抢

1.1    同步对象锁synchronized

public classDressingRoom {

public void use(){

System.out.println(Thread.currentThread().getName()+"进入试衣间");

try {

Thread.sleep(1000);

} catch (InterruptedExceptione) {

// TODOAuto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"离开试衣间");

}

}

public classCustomerRunnable implements Runnable {

private DressingRoom room;

public CustomerRunnable(DressingRoomroom) {

this.room = room;

}

@Override

public void run() {

// TODO Auto-generated methodstub

for(int i=0;i<10;i++){

synchronized (room) {//获取试衣间的锁,其他顾客、线程不能再进入

room.use();//使用试衣间

}//释放锁,其他顾客可以进入

try {

Thread.sleep(1000);//挑选、休息

} catch(InterruptedException e) {

// TODOAuto-generated catch block

e.printStackTrace();

}

}

}

}

DressingRoom room =new DressingRoom();

CustomerRunnable r = newCustomerRunnable(room);

Thread zhangsan = newThread(r);

Thread lisi = new Thread(r);

zhangsan.setName("张三");

lisi.setName("李四");

zhangsan.start();

lisi.start();

1.2    线程同步

1.2.1  多线程安全问题

当run()方法体内的代码操作到了成员变量(共享数据)时,就可能会出现多线程安全问题

产生的原因

当一个线程执行操作共享数据的相关的代码块时,其他线程也参与了运算,就会导致线程安全问题的产生

解决思路

将操作共享数据的所有代码封装起来,当有线程在执行这些代码的时候,其他线程不能参与运算

1.2.2  同步的手段

同步代码块

synchronized(锁对象){ // 需要同步的代码 }

同步方法

publicsynchronized void 方法名(参数列表){ // 方法体 }

Lock(JDK1.5新特性)

1.2.3  同步的好处和弊端

好处:解决了线程的安全问题

弊端:降低了效率

1.2.4  同步的前提和准则

同步的前提

必须是多线程并且使用了同一把锁

切记:不要乱用同步处理

同步的准则

1、使用代码保持剪短,把不随线程变化的预处理和后处理移出同步块

2、不要阻塞,比如InputStream.read()

3、在持有锁额时候,不要对其他对象调用同步方法

1.2.5  验证同步锁

非静态方法默认同步锁是当前对象(this

静态方法默认同步锁是当前类的类对象(Class对象)

1.2.6  同步对象锁

public classDressingRoom {

public void use(){

synchronized (this) {

System.out.println(Thread.currentThread().getName()+"进入试衣间");

try {

Thread.sleep(1000);

} catch (InterruptedExceptione) {

// TODOAuto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"离开试衣间");

}

}

}

public classCustomerRunnable implements Runnable {

private DressingRoom room;

public CustomerRunnable(DressingRoomroom) {

this.room = room;

}

@Override

public void run() {

for (int i = 0; i < 10;i++) {

room.use();//使用试衣间

try {

Thread.sleep(1000);//挑选、休息

} catch(InterruptedException e) {

// TODOAuto-generated catch block

e.printStackTrace();

}

}

}

}

DressingRoom room =new DressingRoom();

CustomerRunnable r = newCustomerRunnable(room);

Thread zhangsan = newThread(r);

Thread lisi = new Thread(r);

zhangsan.setName("张三");

lisi.setName("李四");

zhangsan.start();

lisi.start();

同步静态类:

public class Singleton {

privatestatic Singleton s =null;

privateSingleton(){

System.out.println("构造方法");

}

publicstatic Singleton getInstance(){

synchronized(Singleton.class) {

if(s==null){

try{

Thread.sleep(3000);//将问题极端化

}catch (InterruptedException e) {

//TODO Auto-generated catch block

e.printStackTrace();

}

s= new Singleton();

}

}

returns;

}

}

public class SingleThread extends Thread {

public voidrun(){

Singleton.getInstance();

}

}

SingleThread t1 = new SingleThread();

SingleThreadt2 = new SingleThread();

t1.start();

t2.start();

同步类:【

//即使张三和王五,对他们来说图纸是同一张,描述类对象是同一个,所以是在同一个锁上进行同步的

synchronized(Microphone.class) {  //在话筒的描述类对象进行同步

microphone.speakWith();

}

同步对象锁:

synchronized(锁对象){

语句

}

这样我称为: 语句在锁对象上进行了同步,在多线程中,多个在同一锁对向上同步的语句快,同时只能有一份进行执行;而且执行是原子的,一定要执行完才能结束,不能切换到具有相同对象锁的语句块去执行(有例外)

synchronized(this){

语句

}

public classMicrophone {

public void speakWith(){

synchronized(this){//在当前话筒对象上进行同步,一个线程抢到当前话筒后,其他线程就不能再抢

System.out.println(Thread.currentThread().getName()+"拿起话筒");

try {

Thread.sleep(1000);

} catch(InterruptedException e) {

}

System.out.println(Thread.currentThread().getName()+"放下话筒");

}//将当前话筒对象释放掉,其他线程可以抢到它

}

}

语句在当前对象上进行了同步,如果多个线程都在同一个当前对象上同步,那么同一时间只能有一个线程执行这个同步的代码块

1.2.7  同步方法

public classMicrophone {

public synchronized void speakWith(){

System.out.println(Thread.currentThread().getName()+"拿起话筒");

try {

Thread.sleep(1000);

} catch (InterruptedExceptione) {

// TODOAuto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"放下话筒");

}

}

同步方法:

[修饰符] synchronized返回类型方法名(参数列表){

语句

}

相当于:

[修饰符] 返回类型 方法名(参数列表){

synchronized(this){

语句

}

}

归根结底一句话,能不能同步成功,要看同步锁是否是同一个对象

在描述类对象上进行同步,在图纸上进行同步

synchronized(类.class){

语句

}

所有在类.class上进行同步的代码块都会受影响,根本原因是描述类对象只有一份拷贝,所以这些线程都是在同一个对象锁上同步,都会受影响。

[修饰符] staticsynchronized 返回类型方法名(参数){

语句

}

相当于:

[修饰符] static 返回类型 方法名(参数列表){

synchronized(类名.class){

语句

}

}

1.3    同步引起的死锁

多线程的死锁问题:线程卡死的情况

死锁产生的情况有很多,我们这里先例举一个比较常见的情况,就是同步锁的嵌套

如果因为同步锁的嵌套使用导致死锁,那么一般就是同步锁的顺序不一致,只要保证锁的顺序是一样的,就可以避免死锁问题】

public class DinnerRunnableimplements Runnable {

private Object knife;

private Object fork;

public DinnerRunnable(Object knife,Object fork) {

this.knife = knife;

this.fork = fork;

}

@Override

public void run() {

synchronized (knife) {

System.out.println(Thread.currentThread().getName()+"拿到刀");

try {

Thread.sleep(100);//问题极端化

} catch(InterruptedException e) {

// TODOAuto-generated catch block

e.printStackTrace();

}

synchronized (fork){

System.out.println(Thread.currentThread().getName()+"拿到叉");

System.out.println(Thread.currentThread().getName()+"吃");

}

}

}

}

public classDinnerRunnable2 implements Runnable {

private Object knife;

private Object fork;

public DinnerRunnable2(Object knife,Object fork) {

this.knife = knife;

this.fork = fork;

}

@Override

public void run() {

synchronized (fork) {

System.out.println(Thread.currentThread().getName()+"拿到叉");

try {

Thread.sleep(100);//问题极端化

} catch(InterruptedException e) {

// TODOAuto-generated catch block

e.printStackTrace();

}

synchronized (knife){

System.out.println(Thread.currentThread().getName()+"拿到刀");

System.out.println(Thread.currentThread().getName()+"吃");

}

}

}

}

Object knife = newObject();

Object fork = new Object();

Thread zhangsan = newThread(new DinnerRunnable(knife, fork));

Thread lisi = new Thread(newDinnerRunnable2(knife, fork));

zhangsan.setName("张三");

lisi.setName("李四");

zhangsan.start();

lisi.start();

同步块引起的死锁

两个对象锁A、B,一个线程拿到A等待B,另一个拿了B等待A,形成死锁

解决方式,让这些线程获取对象锁的顺序一致

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容