线程的同步
例如:卖票的问题
package day9.ten;
class Window1 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
} else {
break;
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 w1 = new Window1();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
结果显示,有好多重复的票数,造成了多卖现象,多运行几次,票数还可能是负数
方式一:同步代码块
package day9.ten;
class Window1 implements Runnable{
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 w1 = new Window1();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
结果:线程安全 同步监视器:可以是任何对象,当你不想new对象是,也可以写入this,当前对象来充当同步监视器。
继承Thread实现线程安全
class Window extends Thread{
private static int ticket = 100;
private static Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(getName() + ":票号:" + ticket);
ticket --;
} else {
break;
}
}
}
}
}
public class WindowTest{
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();
}
}
二者原理相同,不过这个需要把同步监视器需要的对象设置为静态的,要不然对象就不唯一了。 同样这个不需要创建对象时,可以使用Window.class,把当前类当作对象充当同步监视器
方式二:同步方法
1.实现Runnable接口的线程
package day9.ten;
class Window3 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
show();
}
}
private synchronized void show(){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
}
}
}
public class WindowTest3 {
public static void main(String[] args) {
Window1 w1 = new Window1();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
2.继承Thread类的线程
package day9.ten;
class Window4 extends Thread{
private static int ticket = 100;
@Override
public void run() {
while(true){
show();
}
}
public static synchronized void show(){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":票号:" + ticket);
ticket --;
}
}
}
public class WindowTest4{
public static void main(String[] args) {
Window4 w1 = new Window4();
Window4 w2 = new Window4();
Window4 w3 = new Window4();
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}
关于同步方法的总结:
- 1.同步方法仍然涉及到同步监视器,只是不需要我们显示的声明
- 2.非静态的同步方法,同步监视器是:this
- 静态的同步方法,同步监视器是:当前类本身
总结
实现Runnable接口的类,充当参数传进去Thread的构造器,不需要把变量,方法设为静态,就可以充当共享资源。而继承Thread类的线程,需要把变量,方法设置为静态的才能充当共享资源。 在线程安全中,主要是每次操作共享资源时,把这个操作锁住,防止其他线程操作,才能实现线程安全。锁是通过对象充当的,用对象当作锁去锁住操作,防止其他线程操作。 同步的方式,解决了线程的安全问题。—好处 操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的的过程,效率低。
|