解决线程安全问题
方式一:同步代码块
synchronized (同步监视器){
//需要被同步的代码
}
说明:
操作共享数据的代码,即为需要被同步的代码
共享数据:多个线程共同操作的变量
同步监视器:俗称,锁。任何一个类的对象都可以充当锁。(要求:多个线程必须要共用同一把锁)
public class Window implements Runnable {
private int ticket = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) { //可以用this,此时的this:唯一的Window的对象
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "***" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
class WindowTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);//3个线程公用1个w对象
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("线程1");
t2.setName("线程2");
t3.setName("线程3");
t1.start();
t2.start();
t3.start();
}
}
public class ThreadTest { public static void main(String[] args) { Window1 t1 = new Window1(); Window1 t2 = new Window1(); Window1 t3 = new Window1(); t1.setName("线程1"); t2.setName("线程2"); t3.setName("线程3"); t1.start(); t2.start(); t3.start(); } } class Window1 extends Thread{ private static int ticket = 100; private static Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { //可以用Window1.class,类也是对象,在内存中只加载一次 if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "***" + ticket); ticket--; } else { break; } } } } }
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的
public class Window implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
sale();
if(ticket==0){
return;
}
}
}
//方法中加synchronized关键字,同步监视器:this
public synchronized void sale(){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "***" + ticket);
ticket--;
}
}
}
class WindowTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("线程1");
t2.setName("线程2");
t3.setName("线程3");
t1.start();
t2.start();
t3.start();
}
}
public class ThreadTest { public static void main(String[] args) { Window1 t1 = new Window1(); Window1 t2 = new Window1(); Window1 t3 = new Window1(); t1.setName("线程1"); t2.setName("线程2"); t3.setName("线程3"); t1.start(); t2.start(); t3.start(); } } class Window1 extends Thread { private static int ticket = 100; @Override public void run() { while (true) { sale(); if (ticket == 0) { return; } } }
//同步监视器:当前类本身Window1.class public static synchronized void sale() { if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "***" + ticket); ticket--; } } }
方式三:lock锁----jdk5.0新增
public class LockTest { public static void main(String[] args) { Window2 w2 = new Window2(); Thread t1 = new Thread(w2); Thread t2 = new Thread(w2); Thread t3 = new Thread(w2); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } } class Window2 implements Runnable { private int ticket = 100; //1.实例化ReentrantLock private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { try { //2.调用锁定方法lock() lock.lock(); if(ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在售票"+ticket); ticket--; }else { break; } }finally{ //3.调用解锁方法unlock() lock.unlock(); } } } }
synchronized和lock异同
相同:都可以解决线程安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动释放锁。
lock需要手动的启动同步,同时结束同步也需要手动的释放