java线程(二):线程同步与同步锁

      在前一篇中已经介绍了如何创建线程以及对线程的五种状态有了基本的认识。本次主要分析线程中一个重要的问题线程同步以及如何同步。

为什么要对线程进行同步

     线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。为了保证有序进行工作、运行,必须要对线程进行同步操作。线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。这样的解释应该是很通俗易懂了。再结合前面多看到线程创建的实例,那种刚开始琢磨不透的输出结果。这里是不是更明白了一些呢?

通常线程同步有这么几种方法

(1)wait():使一个线程处于等待状态,并且释放所持有的对象的lock。

(2)sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉 InterruptedException异常。

(3)notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

(4)notityAll ():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

下面给出一些实例。

   try
        {
          Thread.sleep(500); //等待5秒钟
        }
       catch(InterruptedException e){  }
    }

     sleep方法属于Thread类中方法,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态,因为线程调度机制恢复线程的运行也需要时间,一个线程对象调用了sleep方法之后,并不会释放他所持有的所有对象锁,所以也就不会影响其他进程对象的运行。

通常sleep()在run()方法里面使用,如下:

class compute extends Thread  
{
    int i=0;
    public void run()
    {
        for(int i=0;i<10;i++)
        {
            System.out.println(i);
            try
            {
                sleep(1000);
            }
            catch(Exception e){}
        }
    }
}

这样的好处就是每个1s时间才输出下一个数字。当然我们也可以在捕捉到异常以后做一些有意思的信息提示。

    try
        {
            sleep(10000);//1000s
        }
        catch(Exception e)
        { 
            System.out.println("哦,电话来了");
        }

如果要直接输出异常的信息提示可以在线程启动以后加上t.interrupt();

关于join()、yield()

     join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。yield方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。

同步锁的使用

1)同步块加锁

     为了使线程完整执行完再执行另外一个线程,这样有序切换执行,同步块加锁可以实现这个功能。

class computetest extends Thread  
{
    char ch;
    static Object obj=new Object();
    compute33(char ch)
    {
        this.ch=ch;
    }
    public void print(char ch)
    {
        for(int i=1;i<10;i++)
        {
            System.out.print(ch);
        }
    }

    public void run()
    {
        synchronized(obj)
        {
            for(int i=1;i<10;i++)
            {
                print(ch);
                System.out.println();
            }
        }   
    }
}

     以上就是run方法使用同步块给线程加锁。控制线程循环输出字符。之后再main里面创建两个交替执行的线程就好了。

public static void main(String[] args)
    {
        computetest t=new computetest('a');
        computetest t1=new computetest('b');
        computetest t2=new computetest('c');
        t.start();
        t1.start();
        t2.start();
    }

这样执行结果是:

aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
ccccccccc
ccccccccc
ccccccccc
ccccccccc
ccccccccc
ccccccccc
ccccccccc
ccccccccc
ccccccccc
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb

另外获取当前线程名称用Thread.currentThread().getName()。

2) 同步方法加锁

另外一种常用的方式就是同步方法加锁。

class compute extends Thread 
{
    int i=10;
    static Object obj=new Object();
    synchronized  void print() 
    {   
        System.out.println(Thread.currentThread().getName()+":"+i);
        i--; 
    } 
    public void run()
    { 
        while(i>0)
        { 
            print();
            try 
            { 
                sleep(1000);
            }
            catch(Exception e){}
        }
    }
}

     synchronized void print() 给同步方法加锁,这样不同的线程共享这个print方法时就会有秩序去执行。同样这种方法与同步块加锁是相通的。

public static void main(String[]  args)
    {
        compute t=new compute();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
    }

创建三个线程执行后,结果为:

Thread-1:10
Thread-3:9
Thread-2:8
Thread-1:7
Thread-2:6
Thread-3:5
Thread-1:4
Thread-2:3
Thread-3:2
Thread-1:1

     关于同步锁的使用还是比较广泛的,这一部分通常会与同步方法相结合使用,也是java程序设计中非常重要的一部分。

    原文作者:java锁
    原文地址: https://blog.csdn.net/cai2016/article/details/51786162
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞