一 同步函数

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-多线程安全-锁的更多相关文章

  1. JAVA多线程与锁机制

    JAVA多线程与锁机制 1 关于Synchronized和lock synchronized是Java的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码 ...

  2. Java 多线程:锁(一)

    Java 多线程:锁(一) 作者:Grey 原文地址: 博客园:Java 多线程:锁(一) CSDN:Java 多线程:锁(一) CAS 比较与交换的意思 举个例子,内存有个值是 3,如果用 Java ...

  3. Java 多线程:锁(二)

    Java 多线程:锁(二) 作者:Grey 原文地址: 博客园:Java 多线程:锁(二) CSDN:Java 多线程:锁(二) AtomicLong VS LongAddr VS Synchroni ...

  4. Java 多线程:锁(三)

    Java 多线程:锁(三) 作者:Grey 原文地址: 博客园:Java 多线程:锁(三) CSDN:Java 多线程:锁(三) StampedLock StampedLock其实是对读写锁的一种改进 ...

  5. java多线程----悲观锁与乐观锁

    java多线程中悲观锁与乐观锁思想 一.悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线 ...

  6. (转)java 多线程 对象锁&类锁

    转自:http://blog.csdn.net/u013142781/article/details/51697672 最近工作有用到一些多线程的东西,之前吧,有用到synchronized同步块,不 ...

  7. java多线程之锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁

    转载至:https://blog.csdn.net/zqz_zqz/article/details/70233767 之前做过一个测试,详情见这篇文章<多线程 +1操作的几种实现方式,及效率对比 ...

  8. Java多线程--公平锁与非公平锁

    上一篇文章介绍了AQS的基本原理,它其实就是一个并发包的基础组件,用来实现各种锁,各种同步组件的.它包含了state变量.加锁线程.等待队列等并发中的核心组件,现在我们来看一下多线程获取锁的顺序问题. ...

  9. java多线程编程——锁优化

    并发环境下进行编程时,需要使用锁机制来同步多线程间的操作,保证共享资源的互斥访问.加锁会带来性能上的损坏,似乎是众所周知的事情.然而,加锁本身不会带来多少的性能消耗,性能主要是在线程的获取锁的过程.如 ...

  10. Java多线程之锁优化策略

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6561264.html  锁的优化策略 编码过程中可采取的锁优化的思路有以下几种: 1:减少锁持有时间 例如:对 ...

随机推荐

  1. 【JEECG-Boot 技术文档】新手入门教程

    Jeecg-Boot入门教程必看(新手学习) 1.开发环境搭建 http://jeecg-boot.mydoc.io/?t=345670 开发工具 :https://pan.baidu.com/sha ...

  2. Glassfish安装、基本使用、在idea中配置Glassfish

    Glassfish安装.基本使用. 一.glassfish简介 glassfish是一款web应用服务器,和tomcat一样,也是一款优秀的Servlet容器. 二.glassfish知识点 1.do ...

  3. Delphi XE10百集视频教程计划

    1. 前言 本人现在的职业是Java程序员,一直想学习一个做桌面应用的编程语言,几年前无意中接触到Delphi,比VB功能强大,比C++语法更容易理解,加上Oracle的PL/SQL的底子,最终决定学 ...

  4. day70test

    day_70: #api / urls: from django.conf.urls import url from . import views urlpatterns = [ url(r'^car ...

  5. 对CNN感受野一些理解

    对CNN感受野一些理解 感受野(receptive field)被称作是CNN中最重要的概念之一.为什么要研究感受野呐?主要是因为在学习SSD,Faster RCNN框架时,其中prior box和A ...

  6. Unity 用代码设置UGUI的渲染层级

    用代码设置UGUI渲染无非和三个API有关: 1.SetAsFirstSibling(); 2.SetAsLastSibling(); 3.SetSiblingIndex(n) SetAsFirstS ...

  7. 官网下载eclipse

    百度搜索eclipse,点击官网链接进入官网 进入官网点击Download Packages 根据自己需要选择对应的版本 选择对应的版本进入下图下载页面,然后点击下载即可 下载完成,解压zip包即可使 ...

  8. CA证书制作

    目录 手动制作CA证书 1.安装 CFSSL 2.初始化cfssl 3.创建用来生成 CA 文件的 JSON 配置文件 4.创建用来生成 CA 证书签名请求(CSR)的 JSON 配置文件 5.生成C ...

  9. css3的2D变形

    一.2D变形 1.变形 transform:translate();translateX();translateY();translate(,); 2.过渡 transition:all 1s; 二. ...

  10. Django项目:CRM(客户关系管理系统)--73--63PerfectCRM实现CRM讲师下载作业

    # teacher_urls.py # ————————62PerfectCRM实现CRM讲师讲课记录———————— from django.conf.urls import url from bp ...