Java的线程安全问题,当有多个线程去操作共享资源时会出现线程的安全的问题。
举个例子:去银行取钱,A和他的亲人B共用一张卡,一天,A在手机上使用支付宝,将卡中的3000块提出2000转入支付宝,而在这同时,B在银行使用这张银行卡取钱,取2000。在这个例子中A,B相当于两个不同的线程,共享的资源就是这个账户,当这两个线程同时去操作这个账户时,就会方法安全问题,比如A取了2000,同时B也取了2000,而这个账户只有3000,于是当A,B两个线程执行完取钱的操作时,账户中的金额就变成了负的了,这就是线程的安全问题。
解决方法:使用同步代码块或者是同步方法进行解决。
1. 同步代码块(synchronized):将多个线程会同时操作共享资源的代码放入同步代码块中,上锁,当一个线程拿到这个锁后进入这个代码块中执行操作,其他的线程得等这个线程执行完代码块中的代码后将锁释放后在有所有的线程去抢占这个锁然后进行操作。同步代码块执行完成后会自动释放锁资源。
public class Window {
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
t1.setName("窗口1:");
t2.setName("窗口2:");
t3.setName("窗口3:");
t1.start();
t2.start();
t3.start();
}
}
class MyThread implements Runnable{
private int x = 100;
@Override
public void run() {
while (true){
synchronized(this){
if(x > 0){
System.out.println(Thread.currentThread().getName() + ":票号为:" + x);
x--;
}else {
break;
}
}
}
}
}
synchronized(可以为任意类型的对象)
2. 同步方法:将多个线程可能会操作的共享资源的代码封装到一个方法中在方法的声明的地方加上synchronized的关键字。
消费者和生产者的线程安全问题:
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer p1 = new Producer(clerk);
Consumer c1 = new Consumer(clerk);
Thread tp = new Thread(p1);
tp.setName("生产者1");
Thread tc = new Thread(c1);
tc.setName("消费者1");
tp.start();
tc.start();
}
}
class Clerk {
private int productCount = 0;
//生产产品
public synchronized void produceProduct() {
if(productCount < 20){
productCount++;
System.out.println(Thread.currentThread().getName() + ":开始生产第" + productCount + "个产品");
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if(productCount > 0){
System.out.println(Thread.currentThread().getName() + ":开始消费第" + productCount + "个产品");
productCount--;
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer implements Runnable{
private Clerk Clerk;
public Producer(Clerk Clerk){
this.Clerk = Clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":开始生产产品");
while (true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Clerk.produceProduct();
}
}
}
class Consumer implements Runnable{
private Clerk Clerk;
public Consumer(Clerk Clerk){
this.Clerk = Clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":开始消费产品");
while (true){
try {
Thread.sleep(102);
} catch (InterruptedException e) {
e.printStackTrace();
}
Clerk.consumeProduct();
}
}
}
3. 在JDK5.0版本以后,可以通过显示定义同步锁对象来实现同步。lock需要手动的进行释放锁,而同步锁会自动释放锁
public class Window {
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
t1.setName("窗口1:");
t2.setName("窗口2:");
t3.setName("窗口3:");
t1.start();
t2.start();
t3.start();
}
}
class MyThread implements Runnable{
private int x = 100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();
if(x > 0){
System.out.println(Thread.currentThread().getName() + ":票号为:" + x);
x--;
}else {
break;
}
}finally {
lock.unlock();
}
}
}
}