java 多线程研究:锁的概念
java多线程:锁
java的多线程中的锁是干嘛的呢?在网上找了很多博客,大都是很专业的语言,让我一时间摸不着头脑。下面分三个部分来总结多线程中的锁的概念。
一,基础概念:
多线程在运行的时候可能会遇到这样的问题,多个线程要用到同一个资源,那么可能会出现错乱,比如线程要改动资源里的数据,那么多个线程同时改就乱了套了。就像公共厕所,必须要一个一个接着上,不能两个人或者多个人同时上。那么锁这个东西就是像厕所里的门,一个人在上厕所,锁上了们,那下一个人就不能进去了。同样的,如果我们想让某一个程序或者某一个变量只能同时被一个线程运行,就得给程序上锁。所以上了锁,就能保证线程有秩序的去运行了。
这里补充一个面试常问的问题:进程和线程的区别:进程是某一个具有独立功能的程序的运行活动,它可以申请系统资源,是一个活动的实体。二线程的范围要比进程小,一个进程可以拥有多个线程。我们把进程作为分配资源的基本单位,而把线程作为独立运行和独立调用的基本单位。
二,实现方式:
具体来说呢。首先Object对象,都有自己的一把锁,也就是说,你随便定义一个变量,这个变量就有一把锁,保证自己只能同时被一个线程使用。这是对象锁。如果我们想给一个函数上锁怎么办?函数定义加上关键字synchronized就可以了,这个函数就被上锁了。如果我们想让一段代码块上锁呢?
synchronized(object){
#####要加锁的代码块
}
这样代码块就被上锁了,而synchronized()里面的参数的作用就是提供锁,因为odject对象自己有把锁,被synchronized(object)标记的代码块,自然就被object的锁,锁上了。
那么我们如何给一个类上锁呢?我需要在类的静态成员中添加synchronized,因为类的静态成员,是所有实例共享的,所以给静态成员加锁,就相当于给类加锁。其实类锁的作用并不是给类加锁:给类的普通成员函数加锁,同一个实例对象,的确不可以用多个线程访问加锁的成员函数。但是处于两个实例对象中的不同线程访问加锁的成员函数就不受影响了。所以类锁的概念就是让不同的实例对象中线程,访问静态成员函数也受到限制。
所以总结一下,锁的类型有:对象锁,类锁(实际上也是方法所),方法锁,代码块锁。
看一下代码例子:
测试主类:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package javathreadlock;
import java.lang.Thread;
/**
*
* @author chenyongkang
*/
public class JavaThreadlock {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
//两个测试类的实例对象
ThreadLock lock = new ThreadLock();
ThreadLock lock2 = new ThreadLock();
//同一个对象的两个线程
Test1 t1 = new Test1(lock,1);
Test2 t2 = new Test2(lock,2);
t1.start();
t2.start();
//通过测试结果可以看出,t1,t2线程不能同时执行被synchronized标记的代码块或者方法。
//不同对象的不同线程
Test3 t3 = new Test3(lock2,3);
Test4 t4 = new Test4(lock,4);
//t3.start();
//t4.start();
//通过测试结果可以看出,不同对象的不同线程,执行被synchronized标记的代码块或者方法不受影响,
//而执行被synchronized标记的静态函数,则受到限制
}
}
参与测试的线程类:
class Test1 extends Thread{
public ThreadLock lock;
public int i;
public Test1(ThreadLock lock,int i){
this.lock=lock;
this.i=i;
}
public void run(){
lock.nolock(i);
lock.lock5(i);
//ThreadLock.lock4(i);
}
}
class Test2 extends Thread{
public ThreadLock lock;
public int i;
public Test2(ThreadLock lock,int i){
this.lock=lock;
this.i=i;
}
public void run(){
lock.nolock(i);
//lock.lock5(i);
ThreadLock.lock4(i);
}
}
class Test3 extends Thread{
public ThreadLock lock;
public int i;
public Test3(ThreadLock lock,int i){
this.lock=lock;
this.i=i;
}
public void run(){
lock.nolock(i);
//lock.lock1(i);
//lock.lock2(i);
lock.lock5(i);
ThreadLock.lock4(i);
}
}
class Test4 extends Thread{
public ThreadLock lock;
public int i;
public Test4(ThreadLock lock,int i){
this.lock=lock;
this.i=i;
}
public void run(){
lock.nolock(i);
//lock.lock1(i);
//lock.lock2(i);
lock.lock5(i);
ThreadLock.lock4(i);
}
}
加各种锁的资源类:
class ThreadLock{
//统计类锁加锁次数
public static int i;
//类对象
public Object obj = new Object();
//不加锁的代码块
public void nolock(int thread){
try{
System.out.println("线程"+thread+"正在运行");
Thread.sleep(2000);
}
catch(Exception e){
e.printStackTrace();
}
}
//方法锁
public synchronized void lock1(int thread){
try{
System.out.println("方法锁一正在被线程"+thread+"运行");
Thread.sleep(2000);
System.out.println("方法所一被线程"+thread+"执行完了");
}
catch(Exception e){
e.printStackTrace();
}
}
//代码块锁1
public void lock2(int thread){
synchronized(this){
try{
System.out.println("代码块锁方法一正在被线程"+thread+"运行");
Thread.sleep(2000);
System.out.println("代码块锁方法一正在被线程"+thread+"执行完了");
}
catch(Exception e){
e.printStackTrace();
}
}
}
//代码块锁方法2
public void lock3(int thread){
synchronized(obj){
try{
System.out.println("代码块锁方法二正在被线程"+thread+"运行");
Thread.sleep(2000);
System.out.println("代码块锁方法二正在被线程"+thread+"执行完了");
}
catch(Exception e){
e.printStackTrace();
}
}
}
//类锁方法
public synchronized static void lock4(int thread){
try{
System.out.println("类锁方法正在被线程"+thread+"运行");
Thread.sleep(2000);
System.out.println("类锁方法正在被线程"+thread+"执行完了");
}
catch(Exception e){
e.printStackTrace();
}
}
//代码块锁3
public void lock5(int thread){
synchronized(this){
try{
System.out.println("代码块锁方法三正在被线程"+thread+"运行");
Thread.sleep(2000);
System.out.println("代码块锁方法三正在被线程"+thread+"执行完了");
}
catch(Exception e){
e.printStackTrace();
}
}
}
}
我们把Test1,和Test2的run里面改一下。t1和t2分别执行lock1和lock5,互相不影响,因为类锁和锁函数里面的锁不冲突。而分别执行lock2和lock6,是不可以的,因为lock2和lock6中的代码块参数都是this,这两个代码块共用一个this的锁。分别执行lock1和lock6,也不可以,因为this是指当前类对象的锁,普通函数上的锁也是当前类对象的锁。如果分别执行lock1所以被synchronized标记的代码块,关键看锁是哪一个。同一个参数与的不同代码块,相当于被绑在一起。
三,wait 和sleep的区别。
wait函数是Object的类函数,表示该对象的锁暂时挂起,任何线程都不能使用这个对象,正在使用的线程,也必须交出锁,然后和别的要使用该对象的线程等着。如果要恢复状态,就使用notify函数,然后再等待池里,随便选一个等待的线程来继续运行。
而sleep函数是Thread线程的函数,表示当前线程睡眠多少多少时间。
四,死锁的概念
先简单举个例子,介绍一下死锁,比如有两个线程A,B,和两个对象a,b。现在A正在调用a,调用a之后A想调用b。B正在使用b,调用完b,之后想调动a。于是A,B 两个线程分别抱着a,b的锁不放开,互相等对方放开锁,然后自己就可以执行下一步。于是程序就发生了死锁。我举一个栗子:
线程t1使用了fun1之后想使用fun2,t2使用了fun2之后想使用fun1
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package javadeadlock;
/**
*
* @author chenyongkang
*/
public class Javadeadlock {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ThreadLock lock=new ThreadLock();
Test1 t1=new Test1(lock,1);
Test2 t2=new Test2(lock,2);
t1.start();
t2.start();
}
}
class ThreadLock extends Thread{
private Object obj=new Object();
public synchronized void fun1(int i){
try{
System.out.println("我在使用函数1,我是线程"+i);
Thread.sleep(2000);
System.out.println("我使用完了,我要是使用函数2我是线程"+i);
fun2(i);
}
catch(Exception e){
e.printStackTrace();
}
}
public void fun2(int i){
synchronized(obj){
try{
System.out.println("我在使用函数2,我是线程"+i);
Thread.sleep(2000);
System.out.println("我使用完了,我要使用函数1我是线程"+i);
fun1(i);
}
catch(Exception e){
e.printStackTrace();
}
}
}
}
class Test1 extends Thread{
ThreadLock lock;
int i;
public Test1(ThreadLock lock,int i){
this.lock=lock;
this.i=i;
}
public void run(){
try{
lock.fun1(i);
}catch(Exception e){
e.printStackTrace();
}
}
}
class Test2 extends Thread{
ThreadLock lock;
int i;
public Test2(ThreadLock lock,int i){
this.lock=lock;
this.i=i;
}
public void run(){
try{
lock.fun2(i);
}catch(Exception e){
e.printStackTrace();
}
}
}
结果:卡在那里。
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多线程与锁机制 1 关于Synchronized和lock synchronized是Java的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码 ...
- java多线程编程01---------基本概念
一. java多线程编程基本概念--------基本概念 java多线程可以说是java基础中相对较难的部分,尤其是对于小白,次一系列文章的将会对多线程编程及其原理进行介绍,希望对正在多线程中碰壁的小 ...
- Java多线程系列——锁的那些事
引入 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率. 下面先带大家来总体预览一下锁的分类图 java锁的具体实现类 1.乐观锁 VS 悲观锁 乐观锁与悲观锁是 ...
- (转)java 多线程 对象锁&类锁
转自:http://blog.csdn.net/u013142781/article/details/51697672 最近工作有用到一些多线程的东西,之前吧,有用到synchronized同步块,不 ...
- java多线程----悲观锁与乐观锁
java多线程中悲观锁与乐观锁思想 一.悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线 ...
- java多线程之锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁
转载至:https://blog.csdn.net/zqz_zqz/article/details/70233767 之前做过一个测试,详情见这篇文章<多线程 +1操作的几种实现方式,及效率对比 ...
随机推荐
- python2内置属性
# encoding: utf-8 # module __builtin__ # from (built-in) # by generator 1.145 from __future__ import ...
- Linux常用命令总结--基础命令
系统信息 1.arch 显示机器的处理器架构(1) 2.uname -m 显示机器的处理器架构(2) 3.lsb_release -a 查看操作系统版本 4.top 查看进程 5.free -m 查看 ...
- 奔五的人学ios:swift竟然没有字符串包括,找个简单的解决方法
swift关于字符串的推断中 有前导.有后缀 两个方法.竟然没有包括推断. 经过学习找了个简单的解决方法: extension String { func has(v:String)->Bool ...
- 【能力提升】SQL Server常见问题介绍及高速解决建议
前言 本文旨在帮助SQL Server数据库的使用人员了解常见的问题.及高速解决这些问题.这些问题是数据库的常规管理问题,对于非常多对数据库没有深入了解的朋友提供一个大概的常见问题框架. 以下一些问题 ...
- HTML开发之(块级标签,行内标签,行内块标签)
显示模式的特性: 主要分为两大类: 块级元素:独占一行,对宽高的属性值生效:如果不给宽度,块级元素就默认为浏览器的宽度,即就是100%宽: 行内元素:可以多个标签存在一行,对宽高属性值不生效,完全靠内 ...
- swift--歌曲播放示例
使用MPMoviePlayerController我们可以是进行音乐播放,如下图: 接口的话,我是自己在本地上搭建了个服务器,自己请求自己
- ASPX代码加固小结
1.replace替换 <%@Page Language="C#"%> <% string strID=Request["id"]; strI ...
- Window关闭端口的方法(445/135/137/138/139/3389等)
为防止漏洞被利用,需要采取必要措施,关闭以上端口,以保证系统更加安全. window2003 关闭135端口的方法 要关闭此端口,只需停止DCOM接口服务即达到目的.下面是详细操作过程. 1.打开“组 ...
- 邮件服务器日志:/var/log/maillog
/var/log/maillog会记录包含系统上运行的邮件服务器的日志信息,比如记录 postfix 或 sendmail 运行时的日志信息 [root@localhost ~]# tail /var ...
- N76E003之IAP
修改FLASH数据通常需要很长时间,不像RAM那样可以实时操作.而且擦除.编程或读取FLASH数据需要遵循相当复杂的时序步骤.N76E003提供方便FALSH编程方式,可以帮助用户通过IAP方式,重新 ...