2021年3月2日20点21分
JAVA自学课程笔记8:
synchronized(同步)修饰代码块:
格式:
synchronized(object aa){
//同步代码块
}
含义:判断aa是否已经被其他线程霸占(术语:锁定),如果发现已经被其他线程霸占。则当前线程陷入等待中,如果发现aa没有被其他线程霸占,则当前线程霸占在aa对象,并执行语句内的同步代码块在当前线程执行同步代码块代码时,其他线程将无法再执行同步代码块的代码(因为当前线程已经霸占了aa对象),当前线程执行完同步代码块的代码后,会自动释放对aa对象的霸占,此时其他线程会相互竞争对aa的霸占,最终CPU会选择其中的某个线程执行。霸占住的那个对象术语叫:监听器。
最终导致的结果是:一个线程正在操作某资源的时候,将不允许其它线程操作该资源,即一次只允许一个线程处理该资源。
火车买票程序:
Test1:
class A implements Runnable{
public static int tickets = 100;
public void run(){
while(true){
if(tickets > 0){
System.out.printf("%s线程正在卖出第%d张票", Thread.currentThread().getName(), tickets);
tickets --;
}else{
break;
}
}
}
}
public class Test1{
public static void main(String[] args){
A aa1 = new A();
Thread t1 = new Thread(aa1);
t1.start();
A aa2 = new A();
Thread t2 = new Thread(aa2);
t2.start();
}
}
//虽然用的是static修饰的tickets,但是还是会在执行语句时发生落差,一个对象在不同线程被同时执行且产生时间差,例如1号线程在卖第99张票时,2号线程已经在卖第94张票了,假如正好下一次轮到了1号线程卖,就卖到了第93张。同一个对象被两个线程同时占用,应发程序bug。
Test2:
class A implements Runnable{
public static int tickets = 100;
String str = new String("我是一个字符对象!");
public void run(){
while(true){
synchronized(str){
if(tickets > 0){
System.out.printf("%s线程正在卖出第%d张票\n", Thread.currentThread().getName(), tickets);
tickets --;
}else{
break;
}
}
}
}
}
public class Test2{
public static void main(String[] args){
A aa = new A();
Thread t1 = new Thread(aa);
t1.start();
Thread t2 = new Thread(aa);
t2.start();
}
}
synchronized修饰一个方法时,实际霸占的是该方法的this指针所指的对象(即synchronized修饰一个方法时,实际霸占的是正在调用该方法的对象)当一个线程进入这个方法后,这个方法的大门就会暂时关闭(不许其他线程进入)直到这个线程走出这个方法后,该方法的大门才会敞开。当然,这个关闭只是对于该类的当前实例有效,多个实例的对象仍然可以同时执行。若Test2的run()方法用synchronized修饰,运行结果会是永远只有一个线程在卖票。
生产和消费程序:
class SynStack{
private int cnt = 0;
private char[] data = new char[6];
public synchronized void push(char c){
while(cnt==data.length){
try{
this.wait();
}catch(InterruptedException e){}
}
this.notify();
data[cnt] = c;
cnt++;
System.out.println("produced #: "+c);
}
public synchronized char pop(){
char c;
while(cnt==0){
try{
this.wait();
}catch(InterruptedException e){}
}
this.notify();
cnt--;
System.out.println("consumed *: "+data[cnt]);
return data[cnt];
}
}
class Producer implements Runnable{
private SynStack ss = null;
public Producer(SynStack ss){
this.ss = ss;
}
public void run(){
char c;
for(int i=0; i<20; ++i){
c = (char)('1'+i);
ss.push(c);
}
}
}
class Consumer implements Runnable{
private SynStack ss = null;
public Consumer(SynStack ss){
this.ss = ss;
}
public void run(){
for(int i=0; i<20; ++i){
try{
Thread.sleep(2000);
}catch(InterruptedException e){}
ss.pop();
}
}
}
public class test1{
public static void main(String[] args){
SynStack ss = new SynStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
Thread t1 = new Thread(p);
t1.start();
Thread t2 = new Thread(c);
t2.start();
}
}
//满足一个线程消费,另外一个线程生产且具有同步性。
JAVA自学课程笔记8
最新推荐文章于 2024-09-15 09:47:04 发布
本文详细讲解了Java中`synchronized`关键字的使用,包括代码块锁的原理、火车买票问题的演示、生产和消费问题的同步栈实现,以及如何避免并发问题。通过实例解析,理解了线程同步如何确保同一资源的互斥访问。
摘要由CSDN通过智能技术生成