A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

Java对于多线程的安全问题,提供了专业的解决方式,就是同步代码块。
synchronized(对象)
{
          需要被同步的代码//那些语句在操作共享数据,就进行同步
}
对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程,即使获取了cpu的执行权也进不去。
[java] view plaincopy  
1.    /*   
2.    给卖票程序示例加上同步代码块。
3.    */  
4.    class Ticket implements Runnable  
5.    {  
6.        private int tick=100;  
7.        Object obj = new Object();  
8.        public void run()  
9.        {  
10.            while(true)  
11.            {  
12.                //给程序加同步,即锁  
13.                synchronized(obj)  
14.                {  
15.                    if(tick>0)  
16.                    {  
17.                        try  
18.                        {     
19.                            //使用线程中的sleep方法,模拟线程出现的安全问题  
20.                            //因为sleep方法有异常声明,所以这里要对其进行处理  
21.                            Thread.sleep(10);  
22.                        }  
23.                        catch (Exception e)  
24.                        {  
25.                        }  
26.                        //显示线程名及余票数  
27.                        System.out.println(Thread.currentThread().getName()+"..sale="+tick--);  
28.                    }  
29.                }     
30.            }  
31.        }  
32.    }  
同步的前提:
1.    必须要有两个或者两个以上的线程
2.    必须是多个线程使用同一个锁,比如输入输出都要进行同步,使用同一把锁(比如类的字符码文件对象,保证唯一性)
必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程都需要判断锁,较为消耗资源
如何找多线程安全问题并进行同步:
1.    明确哪些代码是多线程运行代码
2.    明确共享数据
3.    明确多线程运行代码中哪些语句是操作共享数据的(共享数据不能被独立语句操作的就会出现安全问题)
同步函数
函数和同步代码块都是封装,两者结合就是同步函数
把synchronized关键字作为修饰符放在函数上就实现了同步函数,无需对象锁
将需要同步的代码单独封装为一个函数,并加上synchronized关键字就实现了同步函数
函数需要被对象调用,那么函数都有一个所属对象的引用就是this,所以同步函数使用的锁是this
[java] view plaincopy  
1.    class Ticket implements Runnable  
2.    {  
3.        private int tick=100;  
4.        Object obj = new Object();  
5.        public void run()  
6.        {  
7.            while(true)  
8.            {  
9.                show();  
10.            }  
11.        }  
12.      //直接在函数上用synchronized修饰即可实现同步  
13.    public synchronized void show()  
14.    {  
15.            if(tick>0)  
16.            {  
17.            try  
18.            {     
19.                //使用线程中的sleep方法,模拟线程出现的安全问题  
20.                //因为sleep方法有异常声明,所以这里要对其进行处理  
21.                Thread.sleep(10);  
22.            }  
23.            catch (Exception e)  
24.            {  
25.            }  
26.            //显示线程名及余票数  
27.            System.out.println(Thread.currentThread().getName()+"..tick="+tick--);  
28.        }  
29.    }     
30.    }  
静态同步函数的锁
同步函数被静态static修饰后,使用的锁不是this,因为静态方法中不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象:类名.class,该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象,也就是类名.class
参阅单例设计模式-懒汉式
[java] view plaincopy  
1.    class Single  
2.    {  
3.        private static Single s = null;  
4.        private Single(){}  
5.        public static Single getInstance()  
6.        {  
7.            if(s==null)  
8.            {  
9.                synchronized(Single.class)  
10.                {  
11.                    if(s==null)  
12.                        s = new Single();  
13.                }  
14.            }  
15.            return s;  
16.        }  
17.    }//同步代码块加入判断的原因:如果第一个线程进入到第一个if代码块,此时并还没获取锁,此时虚拟机切换给第二个线程,它也进来了if代码块,并且此时获取锁,new一个对象,然后返回。接着虚拟机切换给第一个线程,它这时候才获取锁,此时如果没有第二个if判断,就会再new 一个对象,这就不能保证类的对象唯一性。  
死锁
同步中嵌套同步,两个同步锁不相同,两个线程都持有锁但是不释放,程序就会发生死锁
[java] view plaincopy  
1.    /*
2.    一个死锁程序
3.     */  
4.    //定义一个类来实现Runnable,并复写run方法  
5.    class LockTest implements Runnable  
6.    {  
7.        private boolean flag;  
8.        LockTest(boolean flag)  
9.        {  
10.            this.flag=flag;  
11.        }  
12.        public void run()  
13.        {  
14.            if(flag)  
15.            {  
16.                while(true)  
17.                {  
18.                    synchronized(LockClass.locka)//a锁  
19.                    {  
20.                        System.out.println(Thread.currentThread().getName()+"------if_locka");  
21.      
22.                        synchronized(LockClass.lockb)//b锁  
23.                        {  
24.                            System.out.println(Thread.currentThread().getName()+"------if_lockb");  
25.                        }  
26.                    }  
27.                }  
28.            }  
29.            else  
30.            {  
31.                while(true)  
32.                {  
33.                    synchronized(LockClass.lockb)//b锁  
34.                    {  
35.                        System.out.println(Thread.currentThread().getName()+"------else_lockb");  
36.      
37.                        synchronized(LockClass.locka)//a锁  
38.                        {  
39.                            System.out.println(Thread.currentThread().getName()+"------else_locka");  
40.                        }  
41.                    }  
42.                }  
43.            }  
44.        }  
45.    }  
46.      
47.    //定义两个锁  
48.    class LockClass  
49.    {  
50.        static Object locka = new Object();  
51.        static Object lockb = new Object();  
52.    }  
53.      
54.    class DeadLock  
55.    {  
56.        public static void main(String[] args)  
57.        {  
58.            //创建2个线程,并启动  
59.            new Thread(new LockTest(true)).start();  
60.            new Thread(new LockTest(false)).start();  
61.        }  
62.    }  
运行结果是两个线程都会停在这里等待对方释放锁

线程间的通信
线程间的通讯其实就是多个线程在操作同一个资源,但是操作的动作不同。
为了保证该资源的唯一性,可以考虑单力设计模式(麻烦),或者在每个线程类中通过构造函数传入资源的实例引用,例:
[java] view plaincopy  
1.    class Res {  
2.        String name;  
3.        String sex;  
4.    }  
5.      
6.    class Input implements Runnable{  
7.        private Res r;  
8.        Input(Res r){  
9.            this.r = r;  
10.        }  
11.        public void run(){  
12.            int flag = 0;  
13.            while(true){  
14.                if(flag==0){  
15.                    r.name = "张三";  
16.                    r.sex = "男";  
17.                }else{  
18.                    r.name = "李四";  
19.                    r.sex = "女";  
20.                }  
21.                flag = (flag+1)%2;//交替打印  
22.            }  
23.        }  
24.    }  
25.      
26.    class Output implements Runnable{  
27.        private Res r;  
28.        Output(Res r){  
29.            this.r = r;  
30.        }  
31.        public void run(){  
32.            while(true){  
33.                System.out.println(r.name+"..."+r.sex);  
34.            }  
35.        }  
36.    }  
37.      
38.    public class InputOutputDemo {  
39.        public static void main(String[] args) {  
40.            Res r = new Res();  
41.              
42.            Input in = new Input(r);  
43.            Output out = new Output(r);  
44.              
45.            Thread t1 = new Thread(in);  
46.            Thread t2 = new Thread(out);  
47.              
48.            t1.start();  
49.            t2.start();  
50.        }  


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马