java-多线程安全-锁
一 同步函数
1.1 一般的方法
同步的另一种体现形式:同步函数。
同步函数使用的锁是哪个?
经过分析:大概猜的是this,因为函数必须被对象调用。
验证:
写一个同步代码块,写一个同步函数,如果同步代码块中的锁对象和同步函数中的锁对象是同一个,
就同步了,就没有错误的数据了。如果不是同一个锁对象,就不同步出现错误数据。
让两个线程,一个线程在同步代码块中执行,一个线程在同步函数中执行。
总结:同步函数使用的锁时this。
同步函数和同步代码块有什么区别吗?
同步函数使用的锁是固定的this。当线程任务只需要一个同步时完全可以使用同步函数。
同步代码块使用的锁可以是任意对象。当线程任务中需要多个同步时,必须通过锁来区分,这时必须使用同步代码块。
同步代码块较为常用。
package test; class Ticket implements Runnable {
private int tickets = 100;
private Object obj = new Object();
boolean flag = true; public void run() {
while (true) {
sale(); } } public synchronized void sale()// 同步函数,使用的锁对象 this。
{
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "...sale..." + tickets--);// 打印线程名称。
}
}
} class ThreadDemo4 {
public static void main(String[] args) {
Ticket t = new Ticket(); Thread t1 = new Thread(t);
Thread t2 = new Thread(t); t1.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
// 切换标记,之前,让主线程停一会,这时就只有一个t1线程在,它就会执行同步代码块。
t.flag = false; t2.start();
}
}
1.2 静态方法
static 同步函数,使用的锁不是this,而是字节码文件对象, 类名.class
class Ticket implements Runnable
{
private static int tickets = 100;
private Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag){
while(true){
synchronized(Ticket.class){
if(tickets>0){
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...obj..."+tickets--);//打印线程名称。
}
}
}
}
else{
while(true){
this.sale();
}
}
} public static synchronized void sale()//
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...sale..."+tickets--);//打印线程名称。
}
}
}
class ThreadDemo5
{
public static void main(String[] args)
{
Ticket t = new Ticket(); Thread t1 = new Thread(t);
Thread t2 = new Thread(t); t1.start();
try{Thread.sleep(10);}catch(InterruptedException e){}
//切换标记,之前,让主线程停一会,这时就只有一个t1线程在,它就会执行同步代码块。
t.flag = false; t2.start();
}
}
二 单例的安全问题
//饿汉式。 多线程并发饿汉式没问题。
class Single
{
private static final Single s = new Single(); private Single(){} public static Single getInstance()
{
return s;
}
} //懒汉式。
class Single
{
private static Single s = null; private Single(){} /*
并发访问会有安全隐患,所以加入同步机制解决安全问题。
但是,同步的出现降低了效率。
可以通过双重判断的方式,解决效率问题,减少判断锁的次数。
重在分析 */
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
// -->0 -->1
s = new Single();
}
}
return s;
}
} class Demo implements Runnable
{
public void run()
{
Single.getInstance();
}
} class ThreadDemo6
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
三 死锁
同步的另一个弊端:
情况之一:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。
这时容易引发一种现象:死锁。
这种情况能避免就避免掉。
例:一碗饭.一个人拿一个筷子,谁也不放.
//Thread-0
synchronized(obj1)
{
-->thread- obj1
synchronized(obj2)
{ } }
//Thread-1
synchronized(obj2)
{
Thread- obj2
synchronized(obj1)
{ } }
例如
class Ticket implements Runnable
{
private int tickets = ;
private Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag){
while(true){
synchronized(obj){
sale();//this lock;
}
}
}
else{
while(true){
this.sale();
}
}
} public synchronized void sale()//this lock
{
synchronized(obj)//obj lock
{
if(tickets>)
{
try{Thread.sleep();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...sale..."+tickets--);//打印线程名称。
}
}
}
}
class ThreadDemo7
{
public static void main(String[] args)
{
Ticket t = new Ticket(); Thread t1 = new Thread(t);
Thread t2 = new Thread(t); t1.start();
try{Thread.sleep();}catch(InterruptedException e){}
//切换标记,之前,让主线程停一会,这时就只有一个t1线程在,它就会执行同步代码块。
t.flag = false; t2.start();
}
}
又例如
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
} public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLock.LOCKA)
{
System.out.println(Thread.currentThread().getName()+"...if......locka");
synchronized(MyLock.LOCKB)
{
System.out.println(Thread.currentThread().getName()+"...if......lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.LOCKB)
{
System.out.println(Thread.currentThread().getName()+"...else......lockb");
synchronized(MyLock.LOCKA)
{
System.out.println(Thread.currentThread().getName()+"...else......locka");
}
}
}
}
}
}
//定义一个用于存储锁对象类。
class MyLock
{
public static final Object LOCKA = new Object();
public static final Object LOCKB = new Object();
} class DeadLockTest
{
public static void main(String[] args)
{
//创建两个线程任务。
Test t1 = new Test(true);
Test t2 = new Test(false); Thread t11 = new Thread(t1);
Thread t22 = new Thread(t2);
t11.start();
t22.start(); }
}
四 生产者和消费者
多线程中最为常见的应用案例:
生产者消费者问题。
生产和消费同时执行,需要多线程。
但是执行的任务却不相同,处理的资源确实相同的:线程间的通信。
1,描述一下资源。
2,描述生产者,因为具备着自己的任务。
3,描述消费者,因为具备着自己的任务。
问题1:数据错误:已经被生产很早期的商品,才被消费到。
出现线程安全问题,加入了同步解决。使用同步函数。
问题已解决:不会在消费到之前很早期的商品。
问题2:发现了连续生产却没有消费,同时对同一个商品进行多次消费。
希望的结果应该是生产一个商品,就被消费掉。生产下一个商品。
搞清楚几个问题?
生产者什么时候生产呢?消费者什么时候应该消费呢?
当盘子中没有面包时,就生产,如果有了面包,就不要生产。
当盘子中已有面包时,就消费,如果没有面包,就不要消费。
//1,描述资源。属性:商品名称和编号, 行为:对商品名称赋值,获取商品。
class Resource
{
private String name;
private int count = ; //1,提供设置的方法。
publicvoid set(String name)
{
//给成员变量赋值并加上编号。
this.name = name + count;
//编号自增。
count++;
//打印生产了哪个商品。
System.out.println(Thread.currentThread().getName()+"......生产者...."+this.name);
}
publicvoid out()
{
System.out.println(Thread.currentThread().getName()+"....消费者...."+this.name);
}
} //2,描述生产者。
class Producer implements Runnable
{
private Resource r ;
// 生产者一初始化就要有资源,需要将资源传递到构造函数中。
Producer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("面包");
}
}
} //3,描述消费者。
class Consumer implements Runnable
{
private Resource r ;
// 消费者一初始化就要有资源,需要将资源传递到构造函数中。
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
} class ThreadDemo8
{
public static void main(String[] args)
{
//1,创建资源对象。
Resource r = new Resource(); //2,创建线程任务。
Producer pro = new Producer(r);
Consumer con = new Consumer(r); //3,创建线程。
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con); t1.start();
t2.start();
}
}
问题1: 已经被生产很早期的商品,才被消费到。
出现线程安全问题,加入了同步解决。使用同步函数。
问题已解决:不会在消费到之前很早期的商品。
class Resource
{
private String name;
private int count = ; //1,提供设置的方法。
public synchronized void set(String name)
{
//给成员变量赋值并加上编号。
this.name = name + count;
//编号自增。
count++;
//打印生产了哪个商品。
System.out.println(Thread.currentThread().getName()+"......生产者...."+this.name);
}
public synchronized void out()
{
System.out.println(Thread.currentThread().getName()+"....消费者...."+this.name);
}
}
问题2:发现了连续生产却没有消费,同时对同一个商品进行多次消费。
希望的结果应该是生产一个商品,就被消费掉。生产下一个商品。
搞清楚几个问题?
生产者什么时候生产呢?消费者什么时候应该消费呢?
当盘子中没有面包时,就生产,如果有了面包,就不要生产。
当盘子中已有面包时,就消费,如果没有面包,就不要消费。
生产者生产了商品后应该告诉消费者来消费。这时的生产者应该处于等待状态。
消费者消费了商品后,应该告诉生产者,这时消费者处于等待状态。
等待:wait();
告诉:notify();//唤醒
问题解决:实现生产一个消费一个。
4.1 等待/唤醒机制
wait(): 会让线程处于等待状态,其实就是将线程临时存储到了线程池中。
notify():会唤醒线程池中任意一个等待的线程。
notifyAll():会唤醒线程池中所有的等待线程。
记住:这些方法必须使用在同步中,因为必须要标识wait,notify等方法所属的锁。
同一个锁上的notify,只能唤醒该锁上的被wait的线程。
例:等待吃饭
还有银行
为什么这些方法定义在Object类中呢?
因为这些方法必须标识所属的锁,而锁可以是任意对象,任意对象可以调用的方法必然时Object类中的方法。
举例:小朋友抓人游戏
/,描述资源。属性:商品名称和编号, 行为:对商品名称赋值,获取商品。
class Resource
{
private String name;
private int count = ; //定义标记。
private boolean flag = false; //1,提供设置的方法。
public synchronized void set(String name)
{ if(flag)
try{this.wait();}catch(InterruptedException e){}
//给成员变量赋值并加上编号。
this.name = name + count;
//编号自增。
count++;
//打印生产了哪个商品。
System.out.println(Thread.currentThread().getName()+"......生产者...."+this.name); //将标记改为true。
flag = true;
//唤醒消费者。
this.notify();
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"....消费者...."+this.name);
//将标记该为false。
flag = false;
//唤醒生产者。
this.notify();
}
} //2,描述生产者。
class Producer implements Runnable
{
private Resource r ;
// 生产者一初始化就要有资源,需要将资源传递到构造函数中。
Producer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("面包");
}
}
} //3,描述消费者。
class Consumer implements Runnable
{
private Resource r ;
// 消费者一初始化就要有资源,需要将资源传递到构造函数中。
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
} class ThreadDemo9
{
public static void main(String[] args)
{
//1,创建资源对象。
Resource r = new Resource(); //2,创建线程任务。
Producer pro = new Producer(r);
Consumer con = new Consumer(r); //3,创建线程。
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con); t1.start();
t2.start();
}
}
五 多生产和多消费
加入多生产多消费
public static void main(String[] args)
{
//1,创建资源对象。
Resource r = new Resource(); //2,创建线程任务。
Producer pro = new Producer(r);
Consumer con = new Consumer(r); //3,创建线程。
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con); t1.start();
t2.start();
t3.start();
t4.start();
}
问题1 生产了商品没有被消费,同一个商品被消费多次。
Thread-2......生产者....面包40527//没有被消费。
Thread-3......消费者....面包40528
Thread-2....消费者....面包40528
分析过程
被唤醒的线程没有判断标记,造成问题1的产生。
解决:只要让被唤醒的线程必须判断标记就可以了。将if判断标记的方式改为while判断标记。记住:多生产多消费,必须时while判断条件。
class Resource
{
private String name;
private int count = ; //定义标记。
private boolean flag = false; //1,提供设置的方法。
public synchronized void set(String name)//
{ while(flag) //////////
try{this.wait();}catch(InterruptedException e){}// t1等 t2等
//给成员变量赋值并加上编号。
this.name = name + count;//商品1 商品2 商品3
//编号自增。
count++;//2 3 4
//打印生产了哪个商品。
System.out.println(Thread.currentThread().getName()+"......生产者...."+this.name);//生产 商品1 生产商品2 生产商品3 //将标记改为true。
flag = true;
//唤醒消费者。
this.notify();
}
public synchronized void out()//
{
while(!flag)//////////
try{this.wait();}catch(InterruptedException e){}//t3等 //t4等
System.out.println(Thread.currentThread().getName()+"....消费者...."+this.name);//消费 商品1
//将标记该为false。
flag = false;
//唤醒生产者。
this.notify();
}
} //2,描述生产者。
class Producer implements Runnable
{
private Resource r ;
// 生产者一初始化就要有资源,需要将资源传递到构造函数中。
Producer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("面包");
}
}
} //3,描述消费者。
class Consumer implements Runnable
{
private Resource r ;
// 消费者一初始化就要有资源,需要将资源传递到构造函数中。
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
} class ThreadDemo10
{
public static void main(String[] args)
{
//1,创建资源对象。
Resource r = new Resource(); //2,创建线程任务。
Producer pro = new Producer(r);
Consumer con = new Consumer(r); //3,创建线程。
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con); t1.start();
t2.start();
t3.start();
t4.start();
}
}
问题2:发现while判断后,死锁了。
原因:生产方唤醒了线程池中生产方的线程。本方唤醒了本方。
解决:希望本方要唤醒对方,没有对应的方法,所以只能唤醒所有。
六 Condition
Condition/*
jdk1.5以后提供多生产多消费的解决方案。
在java.util.concurrent.locks 软件包中提供相应的解决方案
Lock接口:比同步更厉害,有更多操作。lock():获取锁 unlock():释放锁;
提供了一个更加面对对象的锁,在该锁中提供了更多的显示的锁操作。
替代同步。
升级到JDK1.5,先把同步改成 Lock。
已经将旧锁替换成新锁,那么锁上的监视器方法(wait,notify,notifyAll)也应该替换成新锁的监视器方法。
而jdk1.5中将这些原有的监视器方法封装到了一个Condition对象中。
想要获取监视器方法,需要先获取Condition对象。
Condition对象的出现其实就是替代了Object中的监视器方法。
await();
signal();
signalAll();
将所有的监视器方法替换成了Condition。
功能和ThreadDemo10.java老程序的功能一样,仅仅是用新的对象。改了写法而已。
但是问题依旧;效率还是低。
希望本方可以唤醒对方中的一个。
老程序中可以通过两个锁嵌套完成,但是容易引发死锁。
新程序中,就可以解决这个问题,只用一个锁,
可以在一个锁上加上多个监视器对象。
*/
import java.util.concurrent.locks.*; class Resource
{
private String name;
private int count = ; //定义一个锁对象。
private final Lock lock = new ReentrantLock();
//获取锁上的Condition对象。为了解决本方唤醒对方的问题。可以一个锁创建两个监视器对象。 private Condition produce = lock.newCondition();//负责生产。
private Condition consume = lock.newCondition();//负责消费。 //定义标记。
private boolean flag = false; //1,提供设置的方法。
public void set(String name)//
{
//获取锁。
lock.lock();
try{ while(flag)
try{produce.await();}catch(InterruptedException e){}// t1等 t2等
this.name = name + count;//商品1 商品2 商品3
count++;//2 3 4
System.out.println(Thread.currentThread().getName()+"......生产者...."+this.name);//生产 商品1 生产商品2 生产商品3 //将标记改为true。
flag = true;
//执行的消费者的唤醒。唤醒一个消费者就哦了。
consume.signal();
}finally{ lock.unlock();//一定要执行。
}
}
public void out()//
{ lock.lock();
try{
while(!flag)
try{consume.await();}catch(InterruptedException e){}//t3等 //t4等
System.out.println(Thread.currentThread().getName()+"....消费者...."+this.name);//消费 商品1
//将标记该为false。
flag = false;
//
produce.signal();
}
finally{
lock.unlock();
}
}
} //2,描述生产者。
class Producer implements Runnable
{
private Resource r ;
// 生产者一初始化就要有资源,需要将资源传递到构造函数中。
Producer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("面包");
}
}
} //3,描述消费者。
class Consumer implements Runnable
{
private Resource r ;
// 消费者一初始化就要有资源,需要将资源传递到构造函数中。
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
} class ThreadDemo11
{
public static void main(String[] args)
{
//1,创建资源对象。
Resource r = new Resource(); //2,创建线程任务。
Producer pro = new Producer(r);
Consumer con = new Consumer(r); //3,创建线程。
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con); t1.start();
t2.start();
t3.start();
t4.start();
}
}
java-多线程安全-锁的更多相关文章
- JAVA多线程与锁机制
JAVA多线程与锁机制 1 关于Synchronized和lock synchronized是Java的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码 ...
- Java 多线程:锁(一)
Java 多线程:锁(一) 作者:Grey 原文地址: 博客园:Java 多线程:锁(一) CSDN:Java 多线程:锁(一) CAS 比较与交换的意思 举个例子,内存有个值是 3,如果用 Java ...
- Java 多线程:锁(二)
Java 多线程:锁(二) 作者:Grey 原文地址: 博客园:Java 多线程:锁(二) CSDN:Java 多线程:锁(二) AtomicLong VS LongAddr VS Synchroni ...
- Java 多线程:锁(三)
Java 多线程:锁(三) 作者:Grey 原文地址: 博客园:Java 多线程:锁(三) CSDN:Java 多线程:锁(三) StampedLock StampedLock其实是对读写锁的一种改进 ...
- java多线程----悲观锁与乐观锁
java多线程中悲观锁与乐观锁思想 一.悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线 ...
- (转)java 多线程 对象锁&类锁
转自:http://blog.csdn.net/u013142781/article/details/51697672 最近工作有用到一些多线程的东西,之前吧,有用到synchronized同步块,不 ...
- java多线程之锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁
转载至:https://blog.csdn.net/zqz_zqz/article/details/70233767 之前做过一个测试,详情见这篇文章<多线程 +1操作的几种实现方式,及效率对比 ...
- Java多线程--公平锁与非公平锁
上一篇文章介绍了AQS的基本原理,它其实就是一个并发包的基础组件,用来实现各种锁,各种同步组件的.它包含了state变量.加锁线程.等待队列等并发中的核心组件,现在我们来看一下多线程获取锁的顺序问题. ...
- java多线程编程——锁优化
并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问.加锁会带来性能上的损坏,似乎是众所周知的事情.然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程.如 ...
- Java多线程之锁优化策略
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6561264.html 锁的优化策略 编码过程中可采取的锁优化的思路有以下几种: 1:减少锁持有时间 例如:对 ...
随机推荐
- LUOGU P1342 请柬(最短路)
传送门 解题思路 又是一道语文题,弄清楚题意之后其实就能想出来了,从1跑一遍最短路,把$dis[n]$加入答案.在建个反图跑一遍最短路,把$dis[n]_$加入最短路就行了.第一遍是去的时候,第二遍是 ...
- System.Web.Mvc.HttpStatusCodeResult.cs
ylbtech-System.Web.Mvc.HttpStatusCodeResult.cs 1.程序集 System.Web.Mvc, Version=5.2.3.0, Culture=neutra ...
- 基于SpringBoot+AntDesign的快速开发平台,JeecgBoot 2.0.2 版本发布
Jeecg-Boot 是一款基于SpringBoot+代码生成器的快速开发平台! 采用前后端分离架构:SpringBoot,Ant-Design-Vue,Mybatis,Shiro,JWT. 强大的代 ...
- 《DSP using MATLAB》Problem 8.26
代码: %% ------------------------------------------------------------------------ %% Output Info about ...
- Python学习day36-并发编程(2)
figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max- ...
- Liunx常用命令行(Ubuntu)
关闭防火墙的命令行: 1. 永久性生效 开启:chkconfig iptables on 关闭:chkconfig iptables off 2. 即时生效,重启后失效 开启:service ipta ...
- std::unique_lock与std::lock_guard区别示例
std::lock_guard std::lock_guard<std::mutex> lk(frame_mutex); std::unique_lock<std::mutex> ...
- DBMS的四大特性
- Jeecms6中后台控制层Action如何将值传入前台视图层模板中的?
转载:https://blog.csdn.net/wsm201005030226/article/details/44343069 Jeecms后台控制层如何传值到前台freemarker的? ...
- Bootstrap.之模态框 显示在遮罩层后面
Bootstrap.之模态框 显示在遮罩层后面 问题描述: 在使用bootstrap模态框,弹出的窗口在遮罩层后面,见图: 解决方案: 保证模态框的代码,所在的上一级(父元素)是body标签,即可.例 ...