Java线程同步
当线程在操作ticket的时候,其他线程应处于等待状态,直到该线程操作完才能由其他线程进行处理在java中通过同步机制来解决线程的安全问题
1.同步代码块:
synchronized(同步监视器){
//需要被同步的代码
}
1.1操作共享数据的diamagnetic,即为需要被同步的代码,共享数据:多个线程共同操作的变量,如ticket
1.2同步监视器,俗称锁,任何一个类的对象,都可以,但要求多个线程共用一把锁
1.3线程间通信
1.3.1通过wait(),notify(),notifyAll()实现线程间通信
wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
notify():一旦执行此方法,就会唤醒wait的一个线程,如果有多个线程被wait,就唤醒优先级高的那个
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
1.3.2说明
a.这三个方法线程通信的使用前提是只能使用在同步代码块或同步方法中,lock线程通信有其他的实现方式
b.三个方法的调用者必须是同步监视器,否则会出现异常
c.这三个方法是定义在java.lang.Object类中
类继承方式
public class WindowTest {
public static void main(String[] args) {
Window w1 = new Window();
Window w2 = new Window();
Window w3 = new Window();
w1.setName("Window-1");
w2.setName("Window-2");
w3.setName("Window-3");
w1.start();
w2.start();
w3.start();
}
}
class Window extends Thread{
//共享static静态变量,但仍存在多线程之间同步的问题
private static int ticket = 100;
//多线程需共用一把锁
private static Object obj = new Object();
@Override
public void run(){
while(ticket > 0){
//同步代码块
synchronized (obj) {
//通过wait()和notifyAll()实现线程通信
obj.notifyAll();
if (ticket > 0) {
System.out.println(getName() + ": Tickets is selling,ticket id: " + ticket);
ticket--;
try {
obj.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
} else {
System.out.println("Tickets sold out.");
break;
}
}
// show();
}
}
Runnable接口实现方式:
public class WindowTest2 {
public static void main(String[] args) {
Window2 win2 = new Window2();
Thread wr1 = new Thread(win2);
Thread wr2 = new Thread(win2);
Thread wr3 = new Thread(win2);
wr1.setName("Window-1");
wr2.setName("Window-2");
wr3.setName("Window-3");
wr1.start();
wr2.start();
wr3.start();
}
}
class Window2 implements Runnable{
private static int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (ticket > 0) {
//同步方法
synchronized (obj) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ": Tickets are selling,ticket id: " + ticket);
ticket--;
} else {
System.out.println("Tickets were sold out.");
break;
}
}
//同步方法
//show();
}
}
2.同步方法
通过synchronized对方法进行修饰。
类继承的方式
public class WindowTest {
public static void main(String[] args) {
Window w1 = new Window();
Window w2 = new Window();
Window w3 = new Window();
w1.setName("Window-1");
w2.setName("Window-2");
w3.setName("Window-3");
w1.start();
w2.start();
w3.start();
}
}
class Window extends Thread{
//共享static静态变量,但仍存在多线程之间同步的问题
private static int ticket = 100;
@Override
public void run(){
while(ticket > 0){
show();
}
}
public static synchronized void show(){//同步监视器
if (ticket > 0){
System.out.println(Thread.currentThread().getName() + ": Tickets are selling,ticket id: " + ticket);
ticket--;
}
}
}
Runnable接口实现方式:
public class WindowTest2 {
public static void main(String[] args) {
Window2 win2 = new Window2();
Thread wr1 = new Thread(win2);
Thread wr2 = new Thread(win2);
Thread wr3 = new Thread(win2);
wr1.setName("Window-1");
wr2.setName("Window-2");
wr3.setName("Window-3");
wr1.start();
wr2.start();
wr3.start();
}
}
class Window2 implements Runnable{
private static int ticket = 100;
@Override
public void run() {
while (ticket > 0) {
//同步方法
show();
}
}
//同步方法
public synchronized void show(){
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ": Tickets are selling,ticket id: " + ticket);
ticket--;
}
}
}
3.同步的方式解决了多线程的安全问题,但是运行效率会降低。
同步方法
1.同步方法仍然涉及到同步监视器,知识不需要我们显式声明
2.非静态的同步方法,同步监视器是this
3.静态的同步方法,同步监视器是当前类本身
3.Lock锁
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
public class DeadTest {
public static void main(String[] args) {
Window3 w3 = new Window3();
Thread d1 = new Thread(w3);
Thread d2 = new Thread(w3);
Thread d3 = new Thread(w3);
d1.setName("Window-1");
d2.setName("Window-2");
d3.setName("Window-3");
d1.start();
d2.start();
d3.start();
}
}
class Window3 implements Runnable{
private int ticket = 100;
//实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock(false);//公平锁,进程先进先出,但会大幅度降低运行效率,不推荐使用
@Override
public void run(){
while(true){
try {
//调用锁定方法lock()
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ": Tickets are selling,ticket id: " + ticket);
ticket--;
} else {
System.out.println("Tickets were sold out.");
break;
}
}finally {
//调用解锁方法unlock()
lock.unlock();
}
}
}
}
a.最好两个都不用,使用一种java.util.concurrent包提供的机制,能够帮助用户处理所有与锁相关的代码。(相关代码后续补充)
b.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码。
c.如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁。