跑起一个程序,并不难;难的是,能让程序跑多远!—— 一颗剽悍的种子
JUC并发系列
锁的存在是多线程与并发的核心,只有透彻理解锁,邂逅面试不慌,也能在实际工作中对处理并发问题 随薪锁欲 随心所欲。
一、多个同步方法执行顺序由谁决定
通常情况下,两个线程的执行顺序并不是由先后顺序(自上而下)执行的,而是有锁的存在,用了同一把锁后,才按顺序先后执行。
这里锁的是同一个对象的锁,computer对象的锁,所以谁在前,谁先执行。
Computer computer = new Computer();
1.1手敲代码实现
这里我们写了两个方法(锁了两个方法,也就是同步方法),一个是学习,一个是打游戏。
public class Demo{
public static void main(String[] args) {
Computer computer = new Computer();
new Thread(()->{
computer.study();
},"a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
computer.game();
},"b").start();
}
}
class Computer{
public synchronized void study(){
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("学习");
}
public synchronized void game(){
System.out.println("打游戏");
}
}
运行结果
可以看到谁在前,谁先执行,所以顺序也是先学习,后游戏。
二、多个对象里的同步方法
多个对象锁的同步方法执行顺序依然,但是不同于一个对象锁,多个对象锁会因为休眠时间而影响执行顺序。
2.1手敲代码实现
在上面的代码,我们只锁了一个对象,那么接下增加多一个对象锁,两个对象,同样两个方法,你还会不会回答是先学习后游戏呢?
public class Demo{
public static void main(String[] args) {
Computer computer1 = new Computer();
Computer computer2 = new Computer();
new Thread(()->{
computer1.study();
},"a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
computer2.game();
},"b").start();
}
}
class Computer{
public synchronized void study(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("学习");
}
public synchronized void game(){
System.out.println("打游戏");
}
}
运行结果
正确答案是:先打游戏,然后是学习。
三.同步方法与非同步方法
同步方法与非同步方法不同之处在于,非同步方法不受锁的影响。
3.1手敲代码实现
在上面1.1的代码中,我们增加了一个看电影的非同步方法(普通方法),我们可以来验证一下执行的顺序。
public void movie(){
System.out.println("看电影");
}
public class Demo{
public static void main(String[] args) {
Computer computer = new Computer();
new Thread(()->{
computer.study();
},"a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
computer.game();
},"b").start();
new Thread(()->{
computer.movie();
},"c").start();
}
}
class Computer{
public synchronized void study(){
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("学习");
}
public synchronized void game(){
System.out.println("打游戏");
}
public void movie(){
System.out.println("看电影");
}
}
运行结果
四、静态同步方法
在之前这篇中深入浅出Java内部类,我们谈过static是最先执行,在加载字节码就会自动调用,而且在主方法main之前,比构造方法早,此时非static属性和方法还没有完成初始化呢。
static静态方法在类一加载就有了,所以锁的class本身,也就是锁的是同一个对象。
4.1ハンド型コードの実装
まだ2つのオブジェクトロックを作成していますが、違いは、メソッドがstaticを追加してstaticメソッドになることです。実行の順序を見てみましょう。
public class Demo{
public static void main(String[] args) {
Computer computer = new Computer();
new Thread(()->{
computer.study();
},"a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
computer.game();
},"b").start();
}
}
class Computer{
public static synchronized void study(){
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("学习");
}
public static synchronized void game(){
System.out.println("打游戏");
}
}
演算結果
5.静的同期方式と通常の同期方式の比較
上記の静的同期方法を理解できれば、静的同期方法と通常の同期方法の違いも理解できたと思います。
静的静的同期メソッドはクラスクラスをロックしますが、通常の同期メソッドは呼び出しをロックするため、2つの異なるロックになります。
この一節を理解すると、コードと出力結果も理解できます。
5.1ハンドコードの実装
次のコードはオブジェクトまたは2つのメソッドも作成します。違いは、出力学習が静的同期メソッドであるのに対し、出力プレイングゲームは通常のメソッドであることです。
public class Demo{
public static void main(String[] args) {
Computer computer = new Computer();
new Thread(()->{
computer.study();
},"a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
computer.game();
},"b").start();
}
}
class Computer{
public static synchronized void study(){
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("学习");
}
public synchronized void game(){
System.out.println("打游戏");
}
}
演算結果
静的同期メソッドと通常の同期メソッドが2つの異なるロックであることがわかります。スリープがない場合、静的同期メソッドが最初に実行されます。これは、クラスが読み込まれたときに呼び出されるためです(重要なこと、数え切れないほどの回数)。ただし、スリープ遅延の場合は、遅延のために通常のメソッドが最初に実行されます。
静的同期メソッドと通常のメソッドは同じロックではなく、前者はクラスであり、後者は呼び出し元であることに注意してください。
最後に、6
最後に、より良い読書体験をするために、私が言いたいことすべてを以下に示します。
私は 私の意志に基づいた決定の種であり、私がブログに書いたものは同じ信条
であった深刻な共有です。このブログ投稿を読むことができれば、それは私たちがまだ非常に運命にあることを意味します。私はそれがあなたにいくつかの助けをもたらすことを願っています、作成は簡単ではありません、私の記事の知識を取り除き、あなたの3つの連続した休暇、コメント、フォローなど、私の最大の動機です。