概述
在java中,解决多线程并发访问同一个资源时出现的安全性问题,解决办法如下:
- 同步代码块
- 同步方法
- 锁机制
方式 一:使用同步代码块(使用synchronized修饰的代码块)
synchronized(同步监听的对象){}
public class SynchronizedBlock implements Runnable{
int ticket = 100;//模拟100张票
public void sell(){
synchronized(this){//这个this指的是当前对象。也就保证了同一个锁
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"买走了第"+ticket+"张票");
ticket --;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
sell();
}
}
public static void main(String[] args) {
SynchronizedBlock t = new SynchronizedBlock();
Thread t1 = new Thread(t, "顾客1");
Thread t2 = new Thread(t, "顾客2");
Thread t3 = new Thread(t, "顾客3");
Thread t4 = new Thread(t, "顾客4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
方式二:使用同步方法(使用synchronized修饰的方法)
synchronized public 返回类型 方法名 () {}
public class SynchronizedMethod implements Runnable{
int ticket = 100;//模拟100张票
synchronized public void sell(){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"买走了第"+ticket+"张票");
ticket --;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
sell();
}
}
public static void main(String[] args) {
SynchronizedMethod t = new SynchronizedMethod();
Thread t1 = new Thread(t, "顾客1");
Thread t2 = new Thread(t, "顾客2");
Thread t3 = new Thread(t, "顾客3");
Thread t4 = new Thread(t, "顾客4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
方式三:使用锁机制 (从java1.5开始使用)
ReentrantLock:一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监听器锁相同的一些基本行为和语音,但是功能更加强大。
import java.util.concurrent.locks.ReentrantLock;
public class SynchronizedLock implements Runnable{
int ticket = 100;
private final ReentrantLock lock = new ReentrantLock();//参数默认false,不公平锁
public void sell(){
lock.lock();
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"买走了第"+ticket+"张票");
ticket --;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
sell();
}
}
public static void main(String[] args) {
SynchronizedLock t = new SynchronizedLock();
Thread t1 = new Thread(t, "顾客1");
Thread t2 = new Thread(t, "顾客2");
Thread t3 = new Thread(t, "顾客3");
Thread t4 = new Thread(t, "顾客4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
synchronzied:是一个访问修饰的关键字,能够解决多线程并发安全问题,但是性能低。
在同一方法中:
- 如果使用synchronized修饰,则该方法安全性高,性能低。
- 如果不使用synchronized修饰,则安全性低,性能高。
同步方法的监听对象是谁?
- 若同步方法是非static修饰的方法,此时监听的对象是this。
- 若同步方法是static修饰的方法,此时监听的对象是当前方法所在类的字节码对象(xx.class)。
在多线程同步中,最具有代表性的就是消费者和生产者模型。
步骤:
- 贡献同一个资源-----》共享空间(share)
- 需要一个线程负责向该空间存放资源-----》生产者(producer)
- 需要一个线程负责从该空间取出数钱-----》消费者(consumer)
多线程并发访问同一资源会,可能会造成线程不安全的问题。比如:每次生产的是一个对象,内容为“小红 女 ”,下一个输出为“小明 男”。
如果不采取同步,可能会发生的问题:
解决方法:
让生产者把对象生产完毕之后,消费者才能消费。
让生产过程中,消费者不能访问共享资源。
在消费过程中,生产者也不能访问共享资源。
- 性别紊乱问题。
- 出现重复生产,重复消费的情况。
这时候就用到了线程中的3个方法。
void wait():让当前线程等待。(必须有监听器对象来调用,不然会报错)
void wait(long time):当前线程失去同步监听对象后,等待一段时间
void notify():唤醒在此监听器上的单个线程。
所以就了代码如下:
共享资源:
package com.thread.procon;
public class Resource {
private String name;
private String gender;
static int i = 0;
private boolean isEmpty=true;//判断资源是否为空
synchronized public void pop(){//消费一个蛋糕
if(isEmpty){
try {
this.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
System.out.println(i++ +""+this.name+" "+this.gender);
isEmpty = true;
this.notify();
}
synchronized public void push(String name,String gender){
if(!isEmpty){
try {
this.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
this.name = name;
this.gender = gender;
System.out.println("生产者生产了");
isEmpty = false;
this.notify();
}
}
生产者:
package com.thread.procon;
public class Producer implements Runnable{
Resource resource;
public Producer(Resource resource){
this.resource = resource;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<15;i++){
if(i%2 == 0){
resource.push("小明","男");
}else{
resource.push("小红","女");
}
}
}
}
消费者:
package com.thread.procon;
public class Consumer implements Runnable{
Resource resource;
public Consumer(Resource resource){
this.resource = resource;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<15;i++){
if(i%2 == 0){
resource.pop();
}
}
}
}
测试类:
package com.thread.procon;
public class TestDemo {
public static void main(String[] args) {
Resource resource = new Resource();
Producer p = new Producer(resource);
Consumer c = new Consumer(resource);
new Thread(p,"生产者").start();
new Thread(c,"消费者者").start();
}
}
OK。
最后
以上就是娇气茉莉为你收集整理的多线程之同步问题(三)的全部内容,希望文章能够帮你解决多线程之同步问题(三)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复