序文
まず、マルチスレッドの知識を使用して、映画のチケット販売プロセスをシミュレートし、スレッドの安全性の問題を引き起こします。
/*
需求:某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口
请设计一个程序模拟该电影院售票过程。
使用Thread类实现
分析: 1.三个窗口相当于三个线程
2.100张票属于共享资源
*/
- コード1:マルチスレッド実装方法1の実装
public class 案例演示1 {
public static void main(String[] args) {
CellThread th1 = new CellThread("窗口1");
CellThread th2 = new CellThread("窗口2");
CellThread th3 = new CellThread("窗口3");
th1.start();
th2.start();
th3.start();
}
}
class CellThread extends Thread{
//设置票为静态变量,即共享资源
static int piao = 100;
//提供无参构造
public CellThread() {
}
//创建有参构造,不再调用setName()方法给线程起名字
public CellThread(String name) {
super(name);
}
@Override
public void run() {
while (true){
if(piao>0){
System.out.println(Thread.currentThread().getName()+"正在售卖"+(piao--)+"张票");
}
}
}
}
- コード2:マルチスレッド方式2を使用した実装
public class 案例演示2 {
public static void main(String[] args) {
Myrunable myrunable = new Myrunable();
//我们只创建了一个任务,所以是共同售卖100张票。
Thread th1 = new Thread(myrunable, "窗口1");
Thread th2 = new Thread(myrunable, "窗口2");
Thread th3 = new Thread(myrunable, "窗口3");
th1.start();
th2.start();
th3.start();
}
}
class Myrunable implements Runnable{
static int piao=1000;
@Override
public void run() {
while (true) {
if (piao > 0) {
//模拟了一下网络延迟,发现出现不合理的数据,即出现了线程安全问题。
//1.出现了0票和负数票,是因为线程并发执行导致的
//2,出现了重复票,是因为原子性所导致的。(自行科普)
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "正在售卖" + (piao--) + "张票");
}
}
}
}
上記のコードにはスレッドセーフの問題があります
- スレッドセーフの問題の条件:
- マルチスレッド環境です
- 共有リソースは複数のスレッド間に存在します
- 共有リソースを操作するステートメントは複数あり、共有リソースの操作はアトミック操作ではありません
1.コードブロックを同期してスレッドセーフの問題を解決します
- 同期コードブロックの形式を使用します。
/*
我们使用同步代码块,将可能出现线程安全问题的代码包裹起来。
synchronized (锁对象){
需要同步的代码
}
锁对象:可以是java中的任意一个对象,常new Object()。注意不可以在括号内new对象,这样每个线程持有的不是同一把锁,没有效果。需要将该对象定义为静态成员变量,被线程共享。
需要同步的代码:可能出现线程安全的问题。(千万不可以出现死循环,这样线程会出现阻塞)
*/
public class 使用同步代码块解决线程安全问题 {
public static void main(String[] args) {
Myrunable1 myrunable = new Myrunable1();
Thread th1 = new Thread(myrunable, "窗口1");
Thread th2 = new Thread(myrunable, "窗口2");
Thread th3 = new Thread(myrunable, "窗口3");
th1.start();
th2.start();
th3.start();
}
}
class Myrunable1 implements Runnable {
static int piao = 100;
//注意:多个线程要使用同一把锁
static Object obj=new Object();
@Override
public void run() {
while (true) {
//同步代码块,包裹可能出现线程安全问题的代码块
//锁对象:可以是java中的任意一个对象,常new Object()
synchronized (obj){
if (piao > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "正在售卖" + (piao--) + "张票");
}
}
}
}
}
/*
为什么加了同步代码块就能解决线程安全问题?
某个线程一旦抢占cpu时间片,进入同步代码块,就会持有该锁。
其他线程没有该锁,只能等待,无法并发执行。
当持有锁的线程出了同步代码块,就会释放锁,然后所有线程再次争抢时间片。
但是加锁,数据安全,效率会降低。
*/
第二に、同期方法はスレッドセーフの問題を解決します
- スレッドセーフなコードをメソッドに抽出し、メソッドに同期を追加してメソッドを同期メソッドに変換できるため、ロックオブジェクトを設定する必要はありません。
public class 同步方法解决线程安全问题 {
public static void main(String[] args) {
Myrunable2 myrunable = new Myrunable2();
Thread th1 = new Thread(myrunable, "窗口1");
Thread th2 = new Thread(myrunable, "窗口2");
Thread th3 = new Thread(myrunable, "窗口3");
th1.start();
th2.start();
th3.start();
}
}
class Myrunable2 implements Runnable {
static int piao = 100;
@Override
public void run() {
//1.将可能出现线程安全问题的代码抽成方法
maipiao();
}
//2.在方法上加上synchronized,将该方法变成个同步方法
public synchronized void maipiao(){
while (true){
if (piao > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "正在售卖" + (piao--) + "张票");
}
}
}
}
/*
我们单独使用同步代码块或同步方法都可以解决线程安全问题,
但是我们混合使用同步代码块和同步方法时候,可能不能解决线程安全问题。
那么是因为同步代码块和同步方法使用的不是同一把锁。
同步方法使用的默认锁对象是this(当将同步方法定义成静态方法时,锁对象是当前类对象的字节码对象)
同步代码块使用的锁对象是任意java对象
解决方法:将同步代码块的锁对象与同步方法保持一致。
*/
- ロックオブジェクトに注意してください
- 同期コードブロックのロックオブジェクトは任意のJavaオブジェクトです
- 同期メソッドのロックオブジェクトはこれです
- 静的同期メソッドのロックオブジェクトは、現在のバイトコードオブジェクトです。
同期コードブロックと同期方法を混在させないでください。使用する場合は、同期コードブロックのロックオブジェクトと同期方法のロックオブジェクトの整合性を保ってください。
3、ロックロックはスレッドセーフの問題を解決します
- JDK5以降、新しいロックオブジェクトLockが提供されます。Lockの実装では、同期されたメソッドやステートメントを使用するよりも幅広いロック操作が提供されます。
- ReentrantLockは、そのインターフェイスの実装クラスです。
- void lock()add lock void lock()release lock
- フォーマット:
/*
加锁
lock.lock();
try{
可能出现线程安全的代码
}catch (Exception e){
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
*/
public class jdk15之后lock锁 {
public static void main(String[] args) {
Myrunable3 myrunable = new Myrunable3();
Thread th1 = new Thread(myrunable, "窗口1");
Thread th2 = new Thread(myrunable, "窗口2");
Thread th3 = new Thread(myrunable, "窗口3");
th1.start();
th2.start();
th3.start();
}
}
class Myrunable3 implements Runnable {
static int piao = 100;
//1.创建lock实现类的一个对象
static Lock lock =new ReentrantLock();
@Override
public void run() {
while (true) {
//2.加锁
lock.lock();
try{
if (piao > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "正在售卖" + (piao--) + "张票");
}
}catch (Exception e){
e.printStackTrace();
}finally {
//3.释放锁
lock.unlock();
}
}
}
}