针对两种线程创建方式,有两种不同的同步方式:关键字synchronized:

同步代码块:两种线程创建方式的不同在于对于继承类的线程,同步监视器说要用到的对象必须声明为static,对于实现Runnable接口的线程,可以直接使用this当作同步监视器

同步方法:Runnable接口型,将共享数据的代码块单独拿出来声明为一个方法,将此方法加上synchronized关键字即可,对于继承型,同样声明为方法,不过此方法要声明为static

 1 package ThreadTest01;
 2 
 3 /**对于创建实现Runnable接口的线程
 4  * 1:问题:卖票过程中出现了错票,重票情况,出现了线程的安全问题
 5  * 2:原因:当某个进程尚未操作完成时,另一个进程参与进来也进行操作
 6  * 3:解决:当一个线程a在操作ticket时,其他线程不可以参与进来,等到a操作ticket结束,其他线程才可以进入
 7  * 即使a出现了堵塞,其它线程也不可以进入
 8  * 4:在java中,使用同步机制解决线程安全问题
 9  *
10  *    方式一:同步代码块:
11  *    关键字synchronized(同步监视器){
12  *        //需要被同步的代码
13  *    }
14  *   说明:操作共享数据的代码即为需要被同步的代码
15  *        共享数据:多个线程共同操作的变量 eg:ticket
16  *        同步监视器:俗称'锁'。任何一个类的对象都可以充当锁,
17  *        要求多个线程必须共享同一个锁,也可以用this来代替,不需要重新创建对象,是最方便的方法
18  *        因为run方法属于这个类,这个类只创建了一个对象demo
19  *************************************************************************
20  *   对于通过创建子类继承Thread类的线程:也可以使用同步代码块,方式一样
21  *   synchronized(同步监视器){
22  *  *    //需要被同步的代码
23  *   }
24  *  不一样的在于,创建同步监视器(锁)时,需要声明为static类变量保证创建的多个对象对应的线程都可以共享此锁
25  *    static Object obj=new Object();
26  *   不可以用this代替,因为对象不唯一
27  *
28  *   方式二:同步方法。
29  *   如果操作共享数据的代码完整的在一个方法中,
30  *   实现Runnable接口的线程:可以将此方法创建声明为 private synchronized void Methods();
31  *   子类继承Thread类的线程:将此方法创建声明为 private static synchronized void Methods();
32  *   同步方法依旧涉及到同步监视器,只是对于
33  *   非静态的同步方法,同步监视器时this
34  *   静态的同步方法,同步监视器是当前类对象(涉及到反射)
35  * 5:同步的方式解决的线程的安全问题,但是相当于一个单线程的过程,效率低
36  ************************************************************************
37  */
38 public class Demo implements Runnable{
39     private int ticket = 100;
40    // Object obj=new Object();//充当锁
41     @Override
42     public void run() {
43         while (true) {
44             synchronized (this){//使用this,指代demo因为时唯一的对象
45             if (ticket > 0) {
46                 try {
47                     Thread.sleep(100);
48                 } catch (InterruptedException e) {
49                     e.printStackTrace();
50                 }
51                 System.out.println(Thread.currentThread().getName() + "\t" + ticket);
52                 ticket--;
53             } else {
54                 break;
55             }
56             }
57         }
58     }
59 
60 
61     public static void main(String[] args) {
62         Demo demo = new Demo();//唯一的对象
63         Thread thread = new Thread(demo);
64         Thread thread1 = new Thread(demo);
65         Thread thread2 = new Thread(demo);
66         thread.setName("线程0");
67         thread1.setName("线程1");
68         thread2.setName("线程2");
69         thread.start();
70         thread1.start();
71         thread2.start();
72     }
73 }

 补充:第三种解决线程安全问题的方法:Lock锁,JDK5.0新增

 1 /**
 2  *
 3  *解决线程安全方式3:Lock锁,JDK5.0新增
 4  *
 5  *
 6  *
 7  *
 8  */
 9 
10 public class Demo implements Runnable{
11     private int ticket = 100;
12     private ReentrantLock lock=new ReentrantLock();
13     @Override
14     public void run() {
15         while (true){
16             try {
17         lock.lock();//调用lock方法
18             if (ticket > 0) {
19                 try {
20                     Thread.sleep(100);
21                 } catch (InterruptedException e) {
22                     e.printStackTrace();
23                 }
24                 System.out.println(Thread.currentThread().getName() + "\t" + ticket);
25                 ticket--;
26             }
27         }finally {
28                 lock.unlock();//调用unlock方法
29             }
30             }
31         }
32 
33     public static void main(String[] args) {
34         Demo demo = new Demo();//唯一的对象
35         Thread thread = new Thread(demo);
36         Thread thread1 = new Thread(demo);
37         Thread thread2 = new Thread(demo);
38         thread.setName("线程0");
39         thread1.setName("线程1");
40         thread2.setName("线程2");
41         thread.start();
42         thread1.start();
43         thread2.start();
44     }
45 }