スレッドセーフ
スレッドセーフとは:
マルチスレッド環境では、スレッドセーフは避けられません。Javaでは、synchronizeキーワードを使用してスレッドセーフの問題を解決できます。
スレッドの不安定さの例:
public class Example09 {
public static void main(String[] args) {
BusTicket3 busTicket = new BusTicket3();
//开启四个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
new Thread(busTicket, "窗口3").start();
new Thread(busTicket, "窗口4").start();
}
}
class BusTicket3 implements Runnable {
private int ticket = 10;//车票数量
public void run() {
while (ticket > 0) {
//获取线程的名字
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "正在发第" + ticket-- + "张票");
}
}
}
運用結果:
上記の運用結果からわかるように、4つのチケットウィンドウで同時に10枚のチケットを販売しますが、通常、同じチケットを2回販売することはありません。同じチケットが繰り返し販売される原因となるのは、スレッドの不安定さです。スレッドセーフは毎回発生するわけではなく、セキュリティ上の問題がないという意味でもありません。この問題を解決するために、synchronizeキーワードを使用します。
同期の2つの使用法:
1.コードブロックを同期します
public class Example09 {
public static void main(String[] args) {
BusTicket3 busTicket = new BusTicket3();
//开启四个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
new Thread(busTicket, "窗口3").start();
new Thread(busTicket, "窗口4").start();
}
}
class BusTicket3 implements Runnable {
private int ticket = 10;//车票数量
Object lock = new Object();//定义一个对象,作为同步代码块的锁
public void run() {
while (ticket > 0) {
synchronized (lock) {
//定义同步代码块
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
if (ticket > 0) {
//获取线程的名字
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "正在发第" + ticket-- + "张票");
} else {
break;
}
}
}
}
}
結果を自分で実行して、効果を確認できます
2.同期方法
public class Example09 {
public static void main(String[] args) {
BusTicket3 busTicket = new BusTicket3();
//开启四个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
new Thread(busTicket, "窗口3").start();
new Thread(busTicket, "窗口4").start();
}
}
class BusTicket3 implements Runnable {
private int ticket = 10;//车票数量
public void run() {
while (ticket > 0) {
try {
sellTicket();
if (ticket <= 0) break;
}catch (Exception e){
e.printStackTrace();
}
}
}
//使用synchronized修饰卖票的方法
private synchronized void sellTicket() throws Exception{
if (ticket > 0){
Thread.sleep(500);
//获取线程的名字
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "正在发第" + ticket-- + "张票");
}
}
}
ロックの使用法:
LockはJava1.6で導入されました。Lockの導入により、ロックの操作性が向上します。これはどういう意味ですか?つまり、手動でロックを取得して解放する必要がある場合、タイムアウト取得の取得と同期機能を中断することもできますが、使用の観点からは、ロックは明らかに同期されていないため、便利ですばやく使用できます。
ロックAPI
ロック方式の使用
例:
public class Example10 {
public static void main(String[] args) {
BusTicket4 busTicket = new BusTicket4();
//开启两个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
}
}
//BusTicket4实现Runnable接口
class BusTicket4 implements Runnable {
private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
private int ticket = 10;//车票数量
public void run() {
lock.lock();//加锁
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "在出售第" + i + "张");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();//释放锁
}
}
演算結果:
tryLock()メソッドの使用
tryLock()、ロックはアイドル状態のときにのみ取得できます(ロックが取得されない場合、ロックは待機せず、ロックが取得されない場合、lock()はブロック状態になります)、戻り値がブール型の場合、ロックの取得はtrueです。それ以外の場合はfalseです。
例:
public class Example11 {
public static void main(String[] args) {
BusTicket5 busTicket = new BusTicket5();
//开启两个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
}
}
//BusTicket4实现Runnable接口
class BusTicket5 implements Runnable {
private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
private int ticket = 10;//车票数量
public void run() {
if (lock.tryLock()) {
//lock.tryLock()返回值是一个Boolean值
System.out.println(Thread.currentThread().getName() + ":获得锁");
for (int i = 1; i <= ticket; i++) {
System.out.println(Thread.currentThread().getName() + "在出售第" + i + "张");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}else {
System.out.println(Thread.currentThread().getName() + ":未获得锁");
}
}
}
演算結果:
tryLock(長時間、TimeUnit単位)メソッドの使用
ロックが使用可能な場合、このメソッドはすぐに値trueを返します。ロックが使用できない場合、現在のスレッドはスレッドスケジューリングに対して無効になり、次の3つの条件のいずれかが発生する前に休止状態になります。
- 現在のスレッドがロックを取得します。
- 他のいくつかのスレッドは、現在のスレッドを中断します。
- 待機時間が経過したら、false
インスタンスを返します。
public class Example12 {
public static void main(String[] args) {
BusTicket6 busTicket = new BusTicket6();
//开启两个线程
new Thread(busTicket, "窗口1").start();
new Thread(busTicket, "窗口2").start();
new Thread(busTicket, "窗口3").start();
}
}
//BusTicket4实现Runnable接口
class BusTicket6 implements Runnable {
private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
private int ticket = 10;//车票数量
public void run() {
try {
if (lock.tryLock(1000, TimeUnit.MICROSECONDS)) {
System.out.println(Thread.currentThread().getName() + ":获得锁");
for (int i = 1; i <= ticket; i++) {
System.out.println(Thread.currentThread().getName() + "在出售第" + i + "张");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
} else {
System.out.println(Thread.currentThread().getName() + ":未获得锁");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
いい加減にして!!!