Java多线程之线程同步

一、多线程出现的安全问题:

1、问题的原因:

多个线程执行的不确定性引起执行结果的不稳定。当多条语句在操作同一个线程共享数据时,
一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
2、解决的办法:

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

二、Synchronized的使用方法:

1、操作共享数据的代码,即为需要被同步的代码。不能包含代码多或者少。
2、共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
3、同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
4、要求:多个线程必须要共用同一把锁。

三、同步机制中的锁:

1、同步锁机制:

对于并发工作,你需要某种方式来防止两个任务访问相同的资源(其实就是共享资源竞争)。
防止这种冲突的方法就是当资源被一个任务使用时,在其上加锁。
第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,
就无法访问它了,而在其被解锁之时,另一个任务就可以锁定并使用它了。

2、synchronized的锁是什么:

任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
同步方法的锁:静态方法(类名.class) 、非静态方法(this)
同步代码块:自己指定,很多时候也是指定为this或类名.class

3、注意事项:

必须确保使用同一个资源的多个线程共用一把锁,
这个非常重要,否则就无法保证共享资源的安全。
一个线程类中的所有静态方法共用同一把锁(类名.class) ,
所有非静态方法共用同一把锁(this) ,同步代码块( 指定需谨慎)

例子:创建三个窗口卖票,总票数为100张。使用实现Runnable接口的方式
1、问题:卖票过程中,出现了重票、错票 -->出现了线程的安全问题。
2、问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
3、如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,
其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。
4、在Java中,我们通过同步机制,来解决线程的安全问题。

 1 class Window implements Runnable{
 2
 3     private int ticket = 100;
 4     @Override
 5     public void run() {
 6
 7         while(true){
 8             synchronized (this){
 9                 //此时的this:唯一的Window1的对象
10                 if (ticket > 0) {
11
12                     try {
13                         Thread.sleep(100);
14                     } catch (InterruptedException e) {
15                         e.printStackTrace();
16                     }
17
18                     System.out.println(Thread.currentThread().getName()
19                             + ":卖票,票号为:" + ticket);
20
21                     ticket--;
22                 } else {
23                     break;
24                 }
25             }
26         }
27     }
28 }
29
30
31 public class WindowTest {
32     public static void main(String[] args) {
33         Window1 w = new Window1();
34
35         Thread t1 = new Thread(w);
36         Thread t2 = new Thread(w);
37         Thread t3 = new Thread(w);
38
39         t1.setName("窗口1");
40         t2.setName("窗口2");
41         t3.setName("窗口3");
42
43         t1.start();
44         t2.start();
45         t3.start();
46     }
47 }

使用同步方法解决实现Runnable接口的线程安全问题。
关于同步方法的总结:
1、同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
2、非静态的同步方法,同步监视器是:this。
3、静态的同步方法,同步监视器是:当前类本身。

 1 class Window implements Runnable {
 2     private int ticket = 100;
 3     @Override
 4     public void run() {
 5         while (true) {
 6             show();
 7         }
 8     }
 9
10     private synchronized void show(){
11         //同步监视器:this
12         //synchronized (this){
13
14             if (ticket > 0) {
15
16                 try {
17                     Thread.sleep(100);
18                 } catch (InterruptedException e) {
19                     e.printStackTrace();
20                 }
21
22                 System.out.println(Thread.currentThread().getName()
23                         + ":卖票,票号为:" + ticket);
24
25                 ticket--;
26             }
27     }
28 }
29
30
31 public class WindowTest {
32     public static void main(String[] args) {
33         Window3 w = new Window3();
34
35         Thread t1 = new Thread(w);
36         Thread t2 = new Thread(w);
37         Thread t3 = new Thread(w);
38
39         t1.setName("窗口1");
40         t2.setName("窗口2");
41         t3.setName("窗口3");
42
43         t1.start();
44         t2.start();
45         t3.start();
46     }
47 }

四、线程同步的范围:

1、如何找问题,即代码是否存在线程安全 (非常重要 )
(1)明确哪些代码是多线程运行的代码。
(2)明确多个线程是否有共享数据。
(3)明确多线程运行代码中是否有多条语句操作共享数据。

2、如何解决呢 (非常重要)
对多条操作共享数据的语句,只能让一个线程都执行完,
在执行过程中,其他线程不可以参与执行。
即所有操作共享数据的这些语句都要放在同步范围中。

3、切记:
范围太小:没锁住所有有安全问题的代码
范围太大:没发挥多线程的功能。

4、释放锁的操作:
●当前线程的同步方法、同步代码块执行结束。
●当前线程在同步代码块、同步方法中遇到break、returm终 止了该代码块、该方法的继续执行。
●当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
●当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。

5、不会释放锁的操作:
●线程执行同步代码块或同步方法时,程序调用Thread. sleep()、Thread.yield()方法暂停当前线程的执行。
●线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,
该线程不会释放锁(同步监视器),应尽量避免使用suspend()和resume()来控制线程。

6、线程的死锁问题:
●死锁原因:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,
就形成线程死锁。出现死锁后,不会出现异常或提示,只是所有的线程都处于阻塞状态,无法继续。
●解决方法:
专门的算法或原则、尽量减少同步资源的定义、尽量避免嵌套同步。

五、Lock(锁):

1、从JDK 5.0开始,Java提供 了更强大的线程同步机制一通过显式定 义同
步锁对象来实现同步。同步锁使用Lock对象充当。

2、java.util.concurrent locks.Lock接口是控制多个线程对共享资源进行访问的
工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象
加锁,线程开始访问共享资源之前应先获得Lock对象。

3、Reentrantl _ock类实现了Lock ,它拥有与synchronized相同的并发性和
内存语义,在实现线程安全的控制中,比较常用的是Reentrantl _ock,可以
显式加锁、释放锁。

4、synchronized与Lock的对比:
(1)Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁, 出了作用域自动释放。
(2)Lock只有代码块锁,synchronized有 代码块锁和方法锁。
(3)使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)。
(4)优先使用顺序:Lock→同步代码块(已经进入了方法体,分配了相应资源)→同步方法(在方法体之外)。

原文地址:https://www.cnblogs.com/ZengBlogs/p/12203557.html

时间: 2024-08-29 05:29:54

Java多线程之线程同步的相关文章

java多线程之 ---- 线程同步

java多线程之线程同步 线程同步 定义:同步是指在同一时间段内只能运行一个线程. 分类:同步方法.同步块. 作用:安全解决共享问题. 同步块: 语法: synchronized (同步对象) { 需要同步的代码; } 例子: public class ThreadDemo implements Runnable{ private int ticket = 5; public void run(){ for(int i=1;i<=5;i++){ synchronized (this){ if(t

关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文)

Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享给大家. 一.对于线程同步和同步锁的理解(注:分享了三篇高质量的博客) 以下我精心的挑选了几篇博文,分别是关于对线程同步的理解和如何选择线程锁以及了解线程锁的作用范围. <一>线程同步锁的选择 1. 这里我推荐下Java代码质量改进之:同步对象的选择这篇博文. 2. 以上推荐的博文是以卖火车票为例

java多线程编程——线程同步之同步函数

如何找出线程安全问题: 1.明确那些代码块是多线程运行代码 2.明确共享数据 3.明确多线程运行代码中哪些语句是操作共享数据的 同步函数示例: class Save{ private int sum; public synchronized void add(int n){ sum+=n; System.out.println("sum="+sum); } } class Cus implements Runnable{ private Save b=new Save(); publi

java 多线程 day03 线程同步

package com.czbk.thread; /** * Created by chengtao on 17/12/3. 线程安全问题: 线程安全出现 的根本原因: 1. 存在两个或者两个以上 的线程对象共享同一个资源. 2. 多线程操作共享资源的代码 有多句. 线程安全问题的解决方案: 方式一: 可以使用同步代码块去解决. 格式: synchronized(锁对象){ 需要被同步的代码 } 同步代码块要注意的事项: 1. 锁对象可以是任意的一个对象. 2. 一个线程在同步代码块中sleep

java多线程之线程的同步与锁定(转)

一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. publicclass Foo { privateint x = 100; publicint getX() { return x;     } publicint fix(int y) {         x = x - y; return x;     } } publicclass MyRunnable i

Java多线程之线程的同步

Java多线程之线程的同步 实际开发中我们也经常提到说线程安全问题,那么什么是线程安全问题呢? 线程不安全就是说在多线程编程中出现了错误情况,由于系统的线程调度具有一定的随机性,当使用多个线程来访问同一个数据时,非常容易出现线程安全问题.具体原因如下:   1,多个线程同时访问一个数据资源(该资源称为临界资源),形成数据发生不一致和不完整.   2,数据的不一致往往是因为一个线程中的多个关联的操作(这几个操作合成原子操作)未全部完成. 关于线程安全问题,有一个经典的情景:银行取钱.代码如下: /

JAVA多线程之线程间的通信方式

一,介绍 本总结我对于JAVA多线程中线程之间的通信方式的理解,主要以代码结合文字的方式来讨论线程间的通信,故摘抄了书中的一些示例代码. 二,线程间的通信方式 ①同步 这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信. 参考示例: public class MyObject { synchronized public void methodA() { //do something.... } synchronized public void methodB()

Java多线程之线程的控制

Java多线程之线程的控制 线程中的7 种非常重要的状态:  初始New.可运行Runnable.运行Running.阻塞Blocked.锁池lock_pool.等待队列wait_pool.结束Dead 如果将"锁池"和"等待队列"都看成是"阻塞"状态的特殊情况,那么可以将线程归纳为5个状态: 新建,就绪,运行,阻塞,死亡. ┌--------------------< 阻塞 ↓                    (1)(2)(3)  

Java多线程之线程的通信

Java多线程之线程的通信 在总结多线程通信前先介绍一个概念:锁池.线程因为未拿到锁标记而发生的阻塞不同于前面五个基本状态中的阻塞,称为锁池.每个对象都有自己的锁池的空间,用于放置等待运行的线程.这些线程中哪个线程拿到锁标记由系统决定.前面我们也有T到死锁的概念,线程互相等待其他线程释放锁标记,而又不释放自己的:造成无休止地等待.当出现死锁的时候,我们应该如何解决呢?通过线程间的通信解决. 线程间通信: 多线程之间的通信有2种方式,第一种是使用object类的几个方法,第二种是使用条件变了来控制