概述
在Java中,我们可以将多个线程同时运行,这样可以使得代码执行效率提高,并且在实际开发中运用多线程可以解决许多问题。但是,当多个线程同时运行,要对同一数据操作时。可能会引起线程安全问题。
例如:
//实现多个窗口买票
class Windos extends Thread{
private static int tit=100;
@Override
public void run() {
while(tit>0){
System.out.println(getName()+":"+tit);
tit--;
}
}
public Windos() {
}
public Windos(String name) {
super(name);
}
}
public class Threadshili {
public static void main(String[] args) {
Windos windos1 = new Windos("窗口一");
Windos windos2= new Windos("窗口二");
Windos windos3 = new Windos("窗口三");
windos1.start();
windos2.start();
windos3.start();
}
}
上例中,定义了三个线程。这三个线程都对同一数据 tit 操作。但是,当一个线程在运行过程中操作 tit 尚未完成,其他进程也参与进来操作 tit 。这就使结果出现了数据重复和错乱。
因此,在开发中,我们必须解决这个问题。
解决线程安全问题
同步的方式:解决了线程安全型问题
操作同步代码时,只能有一个线程执行,其他线程无法操作,此时相当于是单线程。效率低–局限性。
同步代码块
- 同步代码块
- synchronized(同步监视器){
- 需要同步的代码
- }
class TreadWin implements Runnable{
int tit=100;
@Override
public void run() {
while(true){
// 阻塞
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//同步代码块
synchronized (this){// synchronized (obj) {
if (tit > 0) {
System.out.println(Thread.currentThread().getName() + "买票,票号为:" + tit);
tit--;
} else
break;
}
}
}
}
说明:
1、操作共享数据的代码即为需要同步的代码,不能包含代码少了(同步安全问题),也不能多了(多线程变单线程)
2、多个线程共同操作的数据即共享数据
3、同步监视器:即 锁。任何一个类的对象都可以充当锁
要求:多个线程共用一个锁
补充:在实现Runnable接口是,同步监视器可以考虑 this
在继承Thread类时。同步监视器可以考虑 子类.class
同步方法
//实现多个窗口买票 :继承Thread类
class Windos2 extends Thread{
private static int tit=100;
@Override
public void run() {
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
show();
}
}
// private synchronized void show(){ 同步监视器:this 不可行
private synchronized static void show(){//同步监视器:Windos2.class
if (tit > 0) {
System.out.println(Thread.currentThread().getName() + "买票,票号为:" + tit);
tit--;
}
}
public Windos2() {
}
public Windos2(String name) {
super(name);
}
}
//同步方法解决 Runnable 接口的线程安全性问题
class TreadWin2 implements Runnable{
int tit=100;
@Override
public void run() {
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
show();}
}
private synchronized void show(){//同步监视器:this
if(tit > 0) {
System.out.println(Thread.currentThread().getName() + "买票,票号为:" + tit);
tit--;
}
}
}
关于同步方法:
1、同步方法中依然涉及到同步监视器;但不需要显示声明
2、对于非静态方法,同步监视器为 this
对于静态方法,同步监视器为 当前类本身(.class)
锁
解决线程同步问题方式三:Lock(锁)
1、实例化 ReentrantLock
2、调用lock方法
3、调用解锁方法:unlock()
class Window implements Runnable{
private int ticket=100;
// 1、实例化 ReentrantLock
private ReentrantLock lock= new ReentrantLock(true);//ctrl p 快捷键
@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();
}
}
}
}
public class LockTest {
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("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
synchronized与lock异同:
- 相同:二者都可以解决线程安全问题
- 不同:synchronized机制在执行相应同步代码以后,自动的释放同步监视器
- lock需要手动的启动同步(lock()),同时结束同步也需要手动实现(unlock())