Java自学课程笔记8
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(); } } //满足一个线程消费,另外一个线程生产且具有同步性。