方式一:同步代码块:
synchronized(同步监视器){
//需要被同步的代码
}
说明: 1. 操作共享数据的代码,即为需要被同步的代码.—>不能包多代码,也别包少代码。
2.共享数据:多个线程共同操作的变量。比如: ticket就是共享数据。
3.同步监视器,俗称:锁。任何一个类的对象,都是可以充当锁的。
要求:多个线程必须要公用同一把锁。
5.同步的方式,解决了线程的安全问题。—好处
操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。—>局限性
补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使this充当同步监视器。
方式二:同步方法:

  • 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。

实现之同步代码块:声明一个共有的对象,把这个对象当成锁。

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 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();
    }
}

实现之同步方法:用show(),把方法设置成synchronized,如果只创建一个对象,代表省略this。

class Window3 implements Runnable{
    private int ticket=100;

    @Override
    public void run() {
        while (true) {
            show();
        }
		}
        private synchronized void show () {//同步监视器:this
            if (ticket > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
                ticket--;
            }
            else
                return ;
    }
}
public class WindowTest3 {
    public static void main(String[] args) {
        day1study.Window3 w=new day1study.Window3();

        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();
    }
}

继承之同步代码块://类可以当作对象使用。

class Window2 extends Thread{

    private static int ticket =100;

    @Override
    public void run() {
        while(true){
                synchronized (Window2.class){
                if (ticket > 0) {

                    System.out.println(getName() + "卖票,票号为:" + ticket);
                    ticket--;
                } else
                    break;
            }

        }

    }
}
public class WindowTest2 {

    public static void main(String[] args) {
        Window2 w1=new Window2();
        Window2 w2=new Window2();
        Window2 w3=new Window2();

        w1.setName("窗口1");
        w2.setName("窗口1");
        w3.setName("窗口1");

        w1.start();
        w2.start();
        w3.start();
    }
}

继承之同步方法:用show(),把方法设置成synchronized,如果只创建一个对象,代表省略this。如果多个对象,就设置成static

class Window4 extends Thread{

    private static int ticket =100;

    @Override
    public void run() {
        while(true){
            show();
       }
    }
    private static synchronized void show(){//同步监视器Window4.class
            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("窗口1");
        w3.setName("窗口1");

        w1.start();
        w2.start();
        w3.start();
    }
}

解决线程安全问题的方式三:Lock ------JDK5.0新增

//TODO 解决线程安全问题的方式三:Lock ------JDK5.0新增
面试题: synchronized与Lock的异同? .
相同:二者都可以解决线程安全问题
不同: synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(Lock()),同时结束同步也需要手动的实现(unLock())
2.优先使用顺序:
Lock→同步代码块(已经进入了方法体,分配了相应资源) >同步方法(在方法体之外)

class Window implements Runnable{
    private int ticket=100;
//    1.实例化
    private ReentrantLock lock=new ReentrantLock();

    @Override
    public void run() {
        while(true){
            try {
//                2.调用锁定lock();
                lock.lock();
                if(ticket>0){
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket);
                    ticket--;
                }else
                    break;
            }finally {
//                3.调用解锁方法 unlock();
                lock.unlock();
            }
        }
    }
}