java线程锁方法和锁对象_java线程同步以及对象锁和类锁解析(多线程synchronized关键字)...

一、关于线程安全

1.是什么决定的线程安全问题?

线程安全问题基本是由全局变量及静态变量引起的。

若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

2.可以解决多线程并发访问资源的方法有哪些?

主要有三种方式:分别是同步代码块 、同步方法和锁机制(Lock)

其中同步代码块和同步方法是通过关键字synchronized实现线程同步

本文主要是将synchronized关键字用法作为例子来去解释Java中的对象锁和类锁

二、synchronized关键字各种用法与实例

事实上,synchronized修饰非静态方法、同步代码块的synchronized (this)用法和synchronized (非this对象)的用法锁的是对象,线程想要执行对应同步代码,需要获得对象锁。

synchronized修饰静态方法以及同步代码块的synchronized (类.class)用法锁的是类,线程想要执行对应同步代码,需要获得类锁。

因此,事实上synchronized关键字可以细分为上面描述的五种用法。

1.同步块synchronized (this)

public class Ticket1 extendsThread{private int nums = 0; //出票数

private int count =20; //剩余

@Overridepublic voidrun() {while (true) {synchronized (this) {if(count <= 0) {break;

}

nums++;

count--;try{

Thread.sleep(550);

}catch(Exception e) {

e.printStackTrace();

}

System.out.println("显示出票信息:"+Thread.currentThread().getName()+

"抢到第"+nums+"张票,剩余"+count+"张");

}

}

}public static voidmain(String[] args) {

Ticket1 ticket1= newTicket1();

Thread anni= new Thread(ticket1,"安妮");

Thread jack= new Thread(ticket1,"jack");

anni.start();

jack.start();

}

}

这样是同步的,线程获取的是同步块synchronized (this)括号()里面的对象实例的对象锁,这里就是Ticket1 实例对象的对象锁了。

需要注意的是synchronized (){}的{}前后的代码依旧是异步的

2.synchronized (非this对象)的用法锁的是对象

例1:

public class Ticket2 implements Runnable{

private int nums = 0; //出票数

private int count =25; //剩余

//实现线程安全三种方法

private final Object lock = new Object();//1.使用私有不变对象锁,使得攻击者无法获取到锁对象(推荐使用)

@Override

public void run() {

while (true) {

//2.this 使用对象自身的锁(隐式锁)--对象锁

//3.Ticket2.class 给Ticket2加锁 --类锁->使用说明:静态方法则一定会同步,非静态方

//法需在单例模式才生效(本例为单例),但是也不能都用静态同步方法,总之用得不好可能会给性能带来极大的影响。

synchronized (lock) {

if(count <= 0) {

break;

}

nums++;

count--;

try {

Thread.sleep(750);

} catch (Exception e) {

e.printStackTrace();

}

System.out.println("显示出票信息:"+Thread.currentThread().getName()+

"抢到第"+nums+"张票,剩余"+count+"张");

}

}

}

public static void main(String[] args) {

Ticket2 ticket2 = new Ticket2();

Thread zhangsan = new Thread(ticket2,"张三");

Thread zhaoyun = new Thread(ticket2,"赵云");

zhangsan.start();

zhaoyun.start();

}

}

例2:

public class Run2 {

public static void main(String[] args) {

Service service = new Service("xiaobaoge");

ThreadA2 a = new ThreadA2(service);

a.setName("A");

a.start();

ThreadB2 b = new ThreadB2(service);

b.setName("B");

b.start();

}

}

class Service {

String anyString = new String();

public Service(String anyString){

this.anyString = anyString;

}

public void setUsernamePassword(String username, String password) {

try {

synchronized (anyString) {

System.out.println("线程名称为:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "进入同步块");

Thread.sleep(3000);

System.out.println("线程名称为:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "离开同步块");

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

class ThreadA2 extends Thread {

private Service service;

public ThreadA2(Service service) {

super();

this.service = service;

}

@Override

public void run() {

service.setUsernamePassword("a", "aa");

}

}

class ThreadB2 extends Thread {

private Service service;

public ThreadB2(Service service) {

super();

this.service = service;

}

@Override

public void run() {

service.setUsernamePassword("b", "bb");

}

}

3.synchronized (class)

public class Run {

public static void main(String[] args) {

ThreadA a = new ThreadA();

a.setName("A");

a.start();

ThreadB b = new ThreadB();

b.setName("B");

b.start();

}

}

class Service {

public static void printA() {

synchronized (Service.class) {

try {

System.out.println("线程名称为:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "进入printA");

Thread.sleep(3000);

System.out.println("线程名称为:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "离开printA");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

public static void printB() {

synchronized (Service.class) {

System.out.println("线程名称为:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "进入printB");

System.out.println("线程名称为:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "离开printB");

}

}

}

4.静态synchronized同步方法

public class Run {

public static void main(String[] args) {

ThreadA a = new ThreadA();

a.setName("A");

a.start();

ThreadB b = new ThreadB();

b.setName("B");

b.start();

}

}

class Service {

synchronized public static void printA() {

try {

System.out.println("线程名称为:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "进入printA");

Thread.sleep(3000);

System.out.println("线程名称为:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "离开printA");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

synchronized public static void printB() {

System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"

+ System.currentTimeMillis() + "进入printB");

System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"

+ System.currentTimeMillis() + "离开printB");

}

}

class ThreadA extends Thread {

@Override

public void run() {

Service.printA();

}

}

class ThreadB extends Thread {

@Override

public void run() {

Service.printB();

}

}

5.synchronized修饰非静态方法

public class Run {

public static void main(String[] args) {

HasSelfPrivateNum numRef = new HasSelfPrivateNum();

ThreadA athread = new ThreadA(numRef);

athread.start();

ThreadB bthread = new ThreadB(numRef);

bthread.start();

}

}

class HasSelfPrivateNum {

private int num = 0;

synchronized public void addI(String username) {

try {

if (username.equals("a")) {

num = 100;

System.out.println("a set over!");

Thread.sleep(2000);

} else {

num = 200;

System.out.println("b set over!");

}

System.out.println(username + " num=" + num);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

class ThreadA extends Thread {

private HasSelfPrivateNum numRef;

public ThreadA(HasSelfPrivateNum numRef) {

super();

this.numRef = numRef;

}

@Override

public void run() {

super.run();

numRef.addI("a");

}

}

class ThreadB extends Thread {

private HasSelfPrivateNum numRef;

public ThreadB(HasSelfPrivateNum numRef) {

super();

this.numRef = numRef;

}

@Override

public void run() {

super.run();

numRef.addI("b");

}

}

实验结论:两个线程访问同一个对象中的同步方法是一定是线程安全的。本实现由于是同步访问,所以先打印出a,然后打印出b

这里线程获取的是HasSelfPrivateNum的对象实例的锁——对象锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值