练习1
错误写法
public class MyRunnable implements Runnable { int ticket=0; @Override public void run() { //循环 //同步代码块 //判断共享数据是否到了末尾,如果到了末尾 //判断共享数据是否到了末尾,如果没有到末尾 while(true){ if (extracted()) break; } } private synchronized boolean extracted() { if (ticket==100){ return true; } else { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ticket++; System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票"); } return false; } }
public class Main { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); //mr是唯一的 锁对象就是唯一的 Thread t1=new Thread(mr); Thread t2=new Thread(mr); t1.setName("窗口1"); t2.setName("窗口2"); t1.start(); t2.start(); } }
在extracted
方法中,使用synchronized
关键字修饰,确保了线程安全,即在同一时刻只有一个线程可以执行该方法。方法中首先判断是否已经卖完了100张票,如果是则返回true
,表示卖票结束;否则,通过Thread.sleep(100)
方法模拟售票过程中的一些耗时操作,然后增加ticket
的值,表示卖出一张票,并打印出当前卖出的票的信息。最后返回false
,表示还未卖完所有的票。
在多核处理器上,不同的线程可以同时在不同的核心上执行,因此一个线程抢到了一个核心的执行权不会直接导致其他线程阻塞,除非线程之间有共享资源竞争或者其他需要同步的操作。
在上述代码中,当一个线程抢到了CPU的执行权时,由于extracted
方法使用了synchronized
关键字修饰,保证了在同一时刻只有一个线程能够执行该方法。因此,其他线程会在尝试执行该方法时被阻塞,直到当前执行的线程执行完毕释放锁。
具体来说,在extracted
方法中,通过synchronized
关键字确保了在同一时刻只有一个线程能够进入该方法执行,其他线程在尝试进入该方法时会被阻塞,直到当前线程执行完毕释放锁。因此,在这段代码中,其他线程会在某一线程执行extracted
方法时被阻塞。
但是,Thread.sleep()
方法并不会释放锁,因此即使一个线程在执行Thread.sleep()
方法时,其他线程仍然无法进入被synchronized
修饰的同步方法或代码块。
换句话说,即使一个线程在执行Thread.sleep()
时,其他线程也无法执行extracted
方法,直到当前线程执行完毕并释放了锁。因此,其他线程在执行extracted
方法时仍然会被阻塞,直到当前线程执行完毕。
正确写法
public class MyRunnable implements Runnable { int ticket=0; @Override public void run() { while (true){ synchronized (MyRunnable.class){ if(ticket==100){ break; } else { try { Thread.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } ticket++; System.out.println(Thread.currentThread().getName()+"正在售出第"+ticket+"张票"); } } } } }
public class Main { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); //mr是唯一的 锁对象就是唯一的 Thread t1=new Thread(mr); Thread t2=new Thread(mr); t1.setName("窗口1"); t2.setName("窗口2"); t1.start(); t2.start(); } }
练习2
你抢一我抢一解法
public class MyRunnable implements Runnable { int gift=100; @Override public void run() { while(true){ synchronized (MyRunnable.class){ if(gift<=10)break; else { try { Thread.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } gift--; System.out.println(Thread.currentThread().getName()+"抢到了第"+(100-gift)+"个礼物"); } MyRunnable.class.notifyAll();//唤醒正在等待的线程 try { MyRunnable.class.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } }
public class Main { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); //mr是唯一的 锁对象就是唯一的 Thread t1=new Thread(mr); Thread t2=new Thread(mr); t1.setName("小明"); t2.setName("小红"); t1.start(); t2.start(); } }
练习3
标准写法
public class MyRunnable implements Runnable { int i=0; @Override public void run() { while(true){ synchronized (MyRunnable.class){ i++; if(i>=100)break; else { if(i%2!=0){ System.out.println(Thread.currentThread().getName()+"获取了奇数"+i); } } } } } }
public class Main { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); //mr是唯一的 锁对象就是唯一的 Thread t1=new Thread(mr); Thread t2=new Thread(mr); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } }
高级写法
线程1首先将锁的对象执行wait()方法 进入沉睡
当线程2将锁对象执行方法notifyAll()时 线程1就会到达锁的外面
以此反复
练习4
import java.util.Random; public class hongbao extends Thread{ static double money=100; static int count=3; static final double MIN=0.01; public hongbao(String name) { this.setName(name); } public hongbao() { } @Override public void run() { /* *同步代码块 *判断 共享数据是否到末尾 是 *判断 共享数据是否到末尾 否 * */ synchronized (hongbao.class){ if(count==0){ System.out.println(getName()+"没有抢到红包"); }else { double price=0; if(count==1){ //表示此时是最后一个红包 剩余的所有钱都是中奖金额 price=money; } else{ //表示不是最后一个红包 获取随机带小数 Random r=new Random(); price=r.nextDouble(money-(count-1)*MIN); if(price<MIN)price=0.01; } money-=price; count--; System.out.println(getName()+"抢到了"+price+"元的红包"); } } } }
public class Main { public static void main(String[] args) { new hongbao("小岩").start(); new hongbao("小栾").start(); new hongbao("小畅").start(); new hongbao("小于").start(); new hongbao("小烨").start(); } }
练习5
import java.util.Random; public class MyRunnable implements Runnable { int arr[]=new int[]{10,5,20,50,100,200,500,800,2,80,300,700}; @Override public void run() { while(true){ synchronized (MyRunnable.class){ Random r=new Random(); int RanNumber=r.nextInt(12);//获得0-11的随机整数 System.out.println(Thread.currentThread().getName()+"获得了"+arr[RanNumber]+"元"); } } } }
public class Main { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); //mr是唯一的 锁对象就是唯一的 Thread t1=new Thread(mr); Thread t2=new Thread(mr); t1.setName("抽奖箱1"); t2.setName("抽奖箱2"); t1.start(); t2.start(); } }
练习6
import java.util.Random; public class MyRunnable implements Runnable { int arr1[]=new int[]{10,20,100,500,2,300}; int arr2[]=new int[]{5,50,200,800,80,700}; int t1=6; int t2=6; int t1Sum=0; int t2Sum=0; boolean t1Judge=false; boolean t2Judge=false; @Override public void run() { while(true){ synchronized (MyRunnable.class){ Random r=new Random(); int RanNumber=r.nextInt(6);//获得0-5的随机整数 if(Thread.currentThread().getName()=="抽奖箱1"){ if(t1==0){ if(t2Judge)break; System.out.println("抽奖结束抽奖箱1获得的总金额为"+t1Sum+"元"); t2Judge=true; } else { System.out.println(Thread.currentThread().getName()+"获得了"+arr1[RanNumber]+"元"); t1--; t1Sum+=arr1[RanNumber]; } } if(Thread.currentThread().getName()=="抽奖箱2"){ if(t2==0){ if(t1Judge)break; System.out.println("抽奖结束抽奖箱2获得的总金额为"+t2Sum+"元"); t1Judge=true; } else { System.out.println(Thread.currentThread().getName()+"获得了"+arr2[RanNumber]+"元"); t2--; t2Sum+=arr2[RanNumber]; } } if(t1Judge&&t2Judge) System.out.println(t1Sum>t2Sum?t1Sum==t2Sum?"两个抽奖箱子获得的钱一样多":"抽奖箱子1比抽奖箱2多抽"+(t1Sum-t2Sum)+"元":"抽奖箱子2比抽奖箱1多抽"+(t2Sum-t1Sum)+"元"); } } } }
public class Main { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); //mr是唯一的 锁对象就是唯一的 Thread t1=new Thread(mr); Thread t2=new Thread(mr); t1.setName("抽奖箱1"); t2.setName("抽奖箱2"); t1.start(); t2.start(); } }
(题目和图片均来自黑马程序员 代码是自己敲的)