练习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();
}
}
(题目和图片均来自黑马程序员 代码是自己敲的)