线程的同步
在写关于线程的同步机制前先来分析两个代码
此处插入TestThread4
package com.atguigu.java1;
/**
* 模拟火车站售票窗口,开启三个窗口售票,总票数为100张
* 继承方式
*
*/
class Window1 implements Runnable{
int ticket=100;
public void run(){
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+":"+ticket--);
}else{
break;
}
}
}
}
public class TestThread4 {
public static void main(String[] args) {
Window1 w=new Window1();
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();
}
}
此处插入TestWindow
package com.atguigu.java1;
/**
* 模拟火车站售票窗口,开启三个窗口售票,总票数为100张
* 实现方式
*/
class Window extends Thread{
static int ticket=100;//票数,声明为static变量是为了共享一个数据,但是static声明以后声明周期较长
public void run(){
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+":"+ticket--);
}else{
break;
}
}
}
}
public class TestWindow {
public static void main(String[] args) {
Window w1=new Window();
Window w2=new Window();
Window w3=new Window();
w1.setName("敞口1");
w2.setName("窗口2");
w3.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}
此程序存在安全问题,在打印车票时会出现重票或错票
那么为什么会出现此问题
首先我们看两张图片
从图中可以看出线程存在安全问题,由于代码中缺少同步机制导致出现错票重票
下面就进入线程同步机制的学习
1.线程安全问题存在的原因?由于一个线程在操作共享数据的过程中,未执行完毕的情况下,另外的线程参与进来,导致共享的数据存在了 安全问题
2.如何解决线程的安全问题?
必须让一个线程操作共享数据完毕后,其他线程才有机会参与共享数据的操作
3.理解互斥锁
在Java语言中,引入了对象互斥锁的概念,来保证共享数据库操作的完整性。
1.每一个对象对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问对象。
2.关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
3.同步的局限性:导致程序的执行效率要降低
4.同步方法(非静态)的锁为this
5.同步方法(静态的)的锁为当前类的本身
方式一:同步代码块
1.sysnchronized(对象){
//需要被同步的代码
}
1.共享数据:多个代码块共同操作同一个数据
2.同步监视器:由一个类的对象来充当。那个线程获取此监视器,谁就执行大括号里被同步的代码
在实现的方式中,考虑同步的话,可以使用this来充当锁。但是在继承的方式中,慎用this
补充:sysnchronized(对象)中的对象可以由任意一个类的对象
例子
/**
* 模拟火车站售票窗口,开启三个窗口售票,总票数为100张
* 同步代码块
*
*/
class Window1 implements Runnable{
int ticket=100;//共享数据
Object obj=new Object();
public void run(){
while(true){
synchronized (obj) {
if (ticket > 0) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket--);
}
}
}
}
}
public class TestThread4 {
public static void main(String[] args) {
Window1 w=new Window1();
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();
}
}
方式二:同步方法
將操作共享数据的方法声明为synchronized.此方法为同步方法,能够保证当其中一个线程执行此方法时其 他线程在外等待直至此线程执行完此方法
>同步方法的锁是this,当前对象
例子
/**
* 模拟火车站售票窗口,开启三个窗口售票,总票数为100张
* 实现方式
*/
class Window2 implements Runnable{
int ticket=100;//共享数据
Object obj=new Object();
public void run(){
while(true){
show();
}
}
public synchronized void show(){
if (ticket > 0) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket--);
}
}
}
public class TestThread5 {
public static void main(String[] args) {
Window2 w=new Window2();
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();
}
}
线程可以当然也许在合适的时候去释放锁
释放锁的操作
1.当前线程的同步方法、同步代码块执行结束
2.当前线程在同步代码快、同步方法中遇到break、return终止了改代码块、该方法继续执行。
3.当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
4.当前线程在同步代块,同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。