java 多线程 synchronized与lock的通信机制等问题,结合相应实例说明

1. 利用多线程实现如下需求:

写两个线程,一个线程打印1~52,另一个线程打印A~Z,打印顺序是12A34B...5152Z;

2. 使用synchronized 实现 

public class Test13
{
    public synchronized void printNum(){
        
//        System.out.println(Thread.currentThread().getName() + "num:	" + Thread.currentThread().getId());
        for (int i = 1; i <= 52; i++)
        {
            System.out.print(i + ",");
            if (i%2 == 0)
            {
                try
                {
                    this.notify();
                    this.wait();
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }
    public synchronized void printLetter(){
//        System.out.println(Thread.currentThread().getName() + "letter:	" + Thread.currentThread().getId());
        for (int i = 0; i < 26; i++)
        {
            try
            {
                System.out.println((char)('A'+i));
                this.notify();
                if (i != 25)
                {
                    this.wait();
                }
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
}

 

public class Test12
{
    public static void main(String[] args) throws InterruptedException
    {
        //写两个线程,一个线程打印1~52,另一个线程打印A~Z,打印顺序是12A34B...5152Z;
//        Test13 test = new Test13();
        Test14 test = new Test14();
        
        Thread t1 = new Thread(new Runnable()
        {
            
            @Override
            public void run()
            {
                try
                {
                    test.printNum();
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                test.printLetter();
            }
        });
        
        t1.start();
        t2.start();
    }
}

 结果:

1,2,A
3,4,B
5,6,C
7,8,D
9,10,E
11,12,F
13,14,G
15,16,H
17,18,I
19,20,J
21,22,K
23,24,L
25,26,M
27,28,N
29,30,O
31,32,P
33,34,Q
35,36,R
37,38,S
39,40,T
41,42,U
43,44,V
45,46,W
47,48,X
49,50,Y
51,52,Z
View Code

2.使用 Lock 实现

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test14
{
    private Lock lock = new ReentrantLock(true);
    private Condition condition = lock.newCondition();

    public void printNum() throws InterruptedException
    {

        try
        {
            System.out.println("num 运行了");
//            if (true)
            if (lock.tryLock(1, TimeUnit.SECONDS))
            {
                System.out.println("num 获得锁了");
//                lock.lock();
                for (int i = 1; i <= 52; i++)
                {
                    System.out.print(i + ",");
                    if (i % 2 == 0)
                    {
                        try
                        {
                            condition.signal();
                            if (i != 52)
                            {
                                condition.await();
                            }
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            lock.unlock();
        }
    }

    public void printLetter()
    {
        try
        {
            System.out.println("Letter 运行了");
//            if (true)
            if (lock.tryLock(1, TimeUnit.SECONDS))
            {
                System.out.println("Letter 获得锁了");
//                lock.lock();
                for (int i = 0; i < 26; i++)
                {
                    try
                    {
                        System.out.println((char) ('A' + i));
                        condition.signal();
                        if (i != 25)
                        {
                            condition.await();
                        }
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            lock.unlock();
        }
    }
}

  结果 :

Letter 运行了
num 运行了
Letter 获得锁了
A
num 获得锁了
1,2,B
3,4,C
5,6,D
7,8,E
9,10,F
11,12,G
13,14,H
15,16,I
17,18,J
19,20,K
21,22,L
23,24,M
25,26,N
27,28,O
29,30,P
31,32,Q
33,34,R
35,36,S
37,38,T
39,40,U
41,42,V
43,44,W
45,46,X
47,48,Y
49,50,Z
51,52,
View Code

明明是先 t1.start(); 但是这里先运行,先得到锁的却是t2. 这里用的是公平锁  Lock lock = new ReentrantLock(true); 保证先申请的线程先获得锁. 这说明线程的运行顺序是由JVM随机分配的,并不是我们代码中先开启就先运行.

二、解读:

1. synchronized  底层使用的也是 reentrantLock ,默认是非公平锁(先申请锁的线程不一定先获得锁),synchronized  使用的线程间的通信机制用的是 wait/notify ,在使用 notify() 或者 notifyAll() 进行通知时,被通知的线程或者顺序是由JVM随机选择的.

2. reentrantLock 默认非公平锁,线程间使用 condition 进行通信.利用 Condition 可以实现 "选择性通知" ,condition.await() 进行阻塞;  condition.signal(); 进行唤醒

Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();

 如上所示,同一个lock 可以产生多个 condition对象.可以用不同的 comdition对象去操作不同的线程.

问题:

在上面的运用场景中当使用 synchronized  时每次都是t1线程先运行,先获得锁.但是使用 lock的 trylock方法时,运行却是相当的不确定,有时是t1先运行,先获得锁,有时是t2. 但是不使用trylock方法而改使用lock.lock()方法时,试了很多次,跟 使用 synchronized  时是一样的.前面有提到线程的运行顺序是JVM随机分配的,在这里似乎又难以解释这种现象.谜!

public class Test15 extends Thread
{
    @Override
    public void run()
    {
        super.run();
        System.out.println(Thread.currentThread().getName() + ": 运行了");
    }
}
public class Test16
{
    public static void main(String[] args)
    {
        Thread[] arr = new Thread[10];
        
        for (int i = 0; i < arr.length; i++)
        {
            arr[i] = new Test15();
            arr[i].setName("线程: " + i);
        }
        
        for (int i = 0; i < arr.length; i++)
        {
            arr[i].start();
        }
    }
}

 结果:

线程: 0: 运行了
线程: 1: 运行了
线程: 4: 运行了
线程: 2: 运行了
线程: 3: 运行了
线程: 6: 运行了
线程: 8: 运行了
线程: 7: 运行了
线程: 5: 运行了
线程: 9: 运行了
View Code

 顺序开启10个线程,但是线程的运行顺序和开启顺序是不一致的. 

原文地址:https://www.cnblogs.com/hoonick/p/10790673.html