精华内容
下载资源
问答
  • 线程间通讯: 其实就是多个线程在操作同一个资源, 但是操作动作不同。 /* **wait: notify();...因为这些方法在操作同步线程时,都必须要标识它们所操作线程只有锁, 只有同一个锁上被等待线

    线程间通讯:
    其实就是多个线程在操作同一个资源,
    但是操作的动作不同。
    /*
    **wait:
    notify();
    notifyAll();**

    都使用在同步中,因为要对持有监视器(锁)的线程操作。
    所以要使用在同步中,因为只有同步才具有锁。

    为什么这些操作线程的方法要定义Object类中呢?
    因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,
    只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
    不可以对不同锁中的线程进行唤醒。
    也就是说,等待和唤醒必须是同一个锁。
    而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
    */

    /*
    对于多个生产者和消费者。
    为什么要定义while判断标记。
    原因:让被唤醒的线程再一次判断标记。

    为什么定义notifyAll
    因为需要唤醒对方线程。
    因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。
    */

    /*
    JDK1.5 中提供了多线程升级解决方案。
    **将同步Synchronized替换成现实Lock操作。
    将Object中的wait,notify notifyAll,替换了Condition对象。**
    该对象可以Lock锁 进行获取。
    该示例中,实现了本方只唤醒对方操作。

    Lock:替代了Synchronized
    lock
    unlock
    newCondition()

    Condition:替代了Object wait notify notifyAll
    await();
    signal();
    signalAll();
    */

    展开全文
  • Java多线程——线程之间的同步摘要:本文主要学习多线程之间是如何同步的,如何使用volatile关键字,如何使用synchronized修饰的同步代码块和同步方法解决线程安全问题。部分内容来自以下博客:...

    Java多线程——线程之间的同步

    摘要:本文主要学习多线程之间是如何同步的,如何使用volatile关键字,如何使用synchronized修饰的同步代码块和同步方法解决线程安全问题。

    部分内容来自以下博客:

    https://www.cnblogs.com/hapjin/p/5492880.html

    https://www.cnblogs.com/paddix/p/5367116.html

    https://www.cnblogs.com/paddix/p/5428507.html

    https://www.cnblogs.com/liuzunli/p/10181869.html

    https://www.cnblogs.com/zhaoyan001/p/6365064.html

    多线程之间的并发问题

    在使用多线程的时候,如果多个线程之间有共享的数据,并且其中一个线程在操作共享数据的时候,其他线程也能操作共享数据,那么就有可能引发线程的并发问题。

    多售票窗口同时售票引发的并发问题

    情景说明:

    有2个售票窗口同时售卖3张车票,在这个情境中,用2个线程模拟2个售票窗口,3张车票是共享资源,可售卖的编号是1到3,从3号车票开始售卖。

    如果在售票时没有考虑线程的并发问题,2个窗口都能同时修改车票资源,则很容易引发多线程的安全问题。

    代码如下:

    1 public classDemo {2 public static voidmain(String[] args) {3 DemoThread dt = newDemoThread();4 Thread t1 = new Thread(dt, "窗口1");5 Thread t2 = new Thread(dt, "窗口2");6 t1.start();7 t2.start();8 }9 }10

    11 class DemoThread implementsRunnable {12 private int ticket = 3;13

    14 @Override15 public voidrun() {16 while (ticket > 0) {17 System.out.println(Thread.currentThread().getName() + " 进入卖票环节 ");18 try{19 Thread.sleep(1);20 } catch(InterruptedException e) {21 e.printStackTrace();22 }23 System.out.println(Thread.currentThread().getName() + " 售卖的车票编号为: " + ticket--);24 }25 }26 }

    运行结果如下:

    1 窗口1 进入卖票环节2 窗口2 进入卖票环节3 窗口1 售卖的车票编号为: 3

    4 窗口2 售卖的车票编号为: 2

    5 窗口1 进入卖票环节6 窗口2 进入卖票环节7 窗口1 售卖的车票编号为: 1

    8 窗口2 售卖的车票编号为: 0

    结果说明:

    从结果中我们看到窗口1在最后一次售卖中,卖出了编号为0的车票,实际上是不存在的。

    出现这种问题的原因是当车票还剩1张的时候,2个窗口同时判断车票数量是否大于1,这时2个窗口就同时进入了售票扣减的代码,导致本来只能卖出1张的车票被2个窗口各自卖出了1张,从而产生了不存在的车票。

    在程序里产生这种问题一般都是因为时间片的切换导致的,当一个线程进入操作共享资源的代码块时,时间片用完,另一个线程也通过判断进入了同一个代码块,导致第二个线程在操作共享资源时,没有重新进行判断。也就是说线程对共享资源的操作时不完整的,中间有可能被其他线程对资源进行修改。

    单例模式的线程安全问题

    ◆ 懒汉式存在线程安全问题

    这种写法起到了延迟加载的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了判断语句块,还没来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例,所以在多线程环境下不可使用这种方式。

    1 public classSingleton {2 private staticSingleton singleton;3

    4 privateSingleton() {}5

    6 public staticSingleton getInstance() {7 if (singleton == null) {8 singleton = newSingleton();9 }10 returnsingleton;11 }12 }

    为了解决线程安全问题,我们可以使用synchronized关键字来修饰获取线程的公有方法,但是这么做会导致每次都要进入到同步方法里判断一下,方法进行同步效率太低。

    1 public classSingleton {2 private staticSingleton singleton;3

    4 privateSingleton() {}5

    6 public static synchronizedSingleton getInstance() {7 if (singleton == null) {8 singleton = newSingleton();9 }10 returnsingleton;11 }12 }

    为了不需要每次都进行同步,可以使用双重检查,只需要在创建的时候进入同步方法,以后只要判断已经存在实例就直接返回实例,不需要再次进入同步方法。

    1 public classSingleton {2 private static volatileSingleton singleton;3

    4 privateSingleton() {}5

    6 public staticSingleton getInstance() {7 if (singleton == null) {8 synchronized (Singleton.class) {9 if (singleton == null) {10 singleton = newSingleton();11 }12 }13 }14 returnsingleton;15 }16 }

    除了使用同步机制保证线程安全之外,还可以使用静态内部类来保证线程安全。

    这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有延迟加载的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。

    类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

    1 public classSingleton {2 privateSingleton() {}3

    4 private static classSingletonInstance {5 private static final Singleton INSTANCE = newSingleton();6 }7

    8 public staticSingleton getInstance() {9 returnSingletonInstance.INSTANCE;10 }11 }

    ◆ 饿汉式不存在线程安全问题

    饿汉式的写法比较简单,就是在类装载的时候就完成实例化,避免了线程同步问题。

    但这样会导致在类加载时就进行了实例化,没有做到延迟加载,如果这个实例没有被用到,会造成内存浪费。

    1 public classSingleton {2 private final static Singleton INSTANCE = newSingleton();3

    4 privateSingleton() {}5

    6 public staticSingleton getInstance() {7 returnINSTANCE;8 }9 }

    产生并发问题的原因

    多个线程操作共享的数据。

    一个线程在操作共享数据时,其他线程也操作了共享数据。

    使用volatile关键字

    可见性

    要想理解volatile关键字,得先了解下JAVA的内存模型:

    每个线程都有一个自己的本地内存空间,线程执行时,先把变量从主内存读取到线程自己的本地内存空间,然后再对该变量进行操作。

    对该变量操作完后,在某个时间再把变量刷新回主内存。

    代码如下:

    1 public classDemo {2 public static voidmain(String[] args) {3 try{4 DemoThread thread = newDemoThread();5 thread.start();6 Thread.sleep(100);7 thread.setRunning(false);8 } catch(InterruptedException e) {9 e.printStackTrace();10 }11 }12 }13

    14 class DemoThread extendsThread {15 private boolean isRunning = true;16

    17 public void setRunning(booleanisRunning) {18 this.isRunning =isRunning;19 }20

    21 @Override22 public voidrun() {23 System.out.println("进入方法");24 while(isRunning) {25 }26 System.out.println("执行完毕");27 }28 }

    运行结果如下:

    1 进入方法

    结果说明:

    线程一直在运行,并没有因为调用了setRunning()方法就停止了运行。

    现在有两个线程,一个是main线程,另一个是RunThread。它们都试图修改isRunning变量。按照JVM内存模型,main线程将isRunning读取到本地线程内存空间,修改后,再刷新回主内存。

    而在JVM设置成-server模式运行程序时,线程会一直在私有堆栈中读取isRunning变量。因此,RunThread线程无法读到main线程改变的isRunning变量,从而出现了死循环,导致RunThread无法终止。

    解决办法就是在isRunning变量上加上volatile关键字修饰,它强制线程从主内存中取volatile修饰的变量。

    代码如下:

    1 private volatile boolean isRunning = true;

    运行结果如下:

    1 进入方法

    2 执行完毕

    有序性

    重排序是指编译器和处理器为了优化程序性能而对指令序列进行排序的一种手段。但是重排序也需要遵守一定规则:

    1)重排序操作不会对存在数据依赖关系的操作进行重排序。

    比如: a=1;b=a; 这个指令序列,由于第二个操作依赖于第一个操作,所以在编译时和处理器运行时这两个操作不会被重排序。

    2)重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变。

    比如: a=1;b=2;c=a+b; 这三个操作,第一步 a=1; 和第二步 b=2; 由于不存在数据依赖关系,所以可能会发生重排序,但是 c=a+b; 这个操作是不会被重排序的,因为需要保证最终的结果一定是c=a+b=3。

    重排序在单线程模式下是一定会保证最终结果的正确性,但是在多线程环境下,问题就出来了。

    但是运行代码并不能找到支持指令重排序的结果,所以这个地方以后还需要补充。

    代码如下:

    1 public classDemo {2 private int count = 1;3 private boolean flag = false;4

    5 public voidwrite() {6 count = 2;7 flag = true;8 }9

    10 public voidread() {11 if(flag) {12 System.out.print(count);13 }14 }15

    16 public static voidmain(String[] args) {17 for (int i = 0; i < 100; i++) {18 Demo demo = newDemo();19 Thread write = new Thread(() ->{20 demo.write();21 });22 Thread read = new Thread(() ->{23 demo.read();24 });25 write.start();26 read.start();27 }28 }29 }

    预测结果说明:

    控制台打印的数据中应该有1出现,但实际情况却只以后2,这个并不能看出程序作了重排序。

    预测有1出现的原因是,为了提供程序并行度,编译器和处理器可能会对指令进行重排序,而在write()方法中由于第一步 count = 2; 和第二步 flag = true; 不存在数据依赖关系,有可能会被重排序。。

    使用volatile关键字修饰共享变量便可以禁止这种重排序。若用volatile修饰共享变量,在编译时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

    原子性

    所谓原子性,就是某系列的操作步骤要么全部执行,要么都不执行。

    volatile只能保证对单次读/写的原子性,不能保证复合类操作的原子性。

    代码如下:

    1 public classDemo {2 public static voidmain(String[] args) {3 DemoThread demoThread = newDemoThread();4 Thread[] threads = new Thread[10];5 for (int i = 0; i < 10; i++) {6 threads[i] = newThread(demoThread);7 threads[i].start();8 }9 try{10 Thread.sleep(1000);11 System.out.println(demoThread.count);12 } catch(InterruptedException e) {13 e.printStackTrace();14 }15 }16 }17

    18 class DemoThread extendsThread {19 public volatile int count = 0;20

    21 @Override22 public voidrun() {23 try{24 Thread.sleep(1);25 } catch(InterruptedException e) {26 e.printStackTrace();27 }28 add();29 }30

    31 private voidadd() {32 for (int i = 0; i < 100; i++) {33 count++;34 }35 }36 }

    运行结果如下:

    1 986

    结果说明:

    在多线程环境下,有可能一个线程将count读取到本地内存中,此时其他线程可能已经将count增大了很多,线程依然对过期的count进行自加,重新写到主存中,最终导致了count的结果不合预期,而是小于1000。

    如果想要在复合类的操作中保证原子性,可用使用synchronized关键字来实现,还可以通过Java并发包中的循环CAS的方式来保证。

    使用synchronized关键字

    synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。

    synchronized的作用有三个:

    ◆ 确保线程互斥的访问同步代码。

    ◆ 保证共享变量的修改能够及时可见。

    ◆ 有效解决重排序问题。

    从语法上讲,synchronized总共有三种用法:

    ◆ 修饰普通方法。

    ◆ 修饰静态方法。

    ◆ 修饰代码块。

    接下来我就通过几个例子程序来说明一下这三种使用方式。

    使用synchronized的同步代码块

    使用synchronized关键字修饰的代码块将对共享资源的操作封装起来,当有一个线程运行代码块时,其他线程只能等待,从而避免共享资源被其他线程修改。

    要求多个线程同步使用的锁都必须是同一个才能保证同步,常用的是使用一个Object对象,或者使用this,或者使用类的class对象。

    代码如下:

    1 public classDemo {2 public static voidmain(String[] args) {3 DemoThread dt = newDemoThread();4 Thread t1 = new Thread(dt, "窗口1");5 Thread t2 = new Thread(dt, "窗口2");6 t1.start();7 t2.start();8 }9 }10

    11 class DemoThread implementsRunnable {12 private int ticket = 3;13

    14 @Override15 public voidrun() {16 while (ticket > 0) {17 try{18 Thread.sleep(1);19 } catch(InterruptedException e) {20 e.printStackTrace();21 }22 synchronized (DemoThread.class) {23 if (ticket > 0) {24 System.out.println(Thread.currentThread().getName() + " 进入卖票环节 ");25 System.out.println(Thread.currentThread().getName() + " 售卖的车票编号为: " + ticket--);26 }27 }28 }29 }30 }

    运行结果如下:

    1 窗口1 进入卖票环节2 窗口1 售卖的车票编号为: 3

    3 窗口2 进入卖票环节4 窗口2 售卖的车票编号为: 2

    5 窗口1 进入卖票环节6 窗口1 售卖的车票编号为: 1

    结果说明:

    线程在进入卖票的代码块之前,先看一下当前是否由其他线程在执行代码块,如果有其他线程在执行代码块则会等待,直到其他线程执行完之后才能进入代码块,从而保证了线程并发的安全问题。

    使用synchronized的普通同步方法

    将操作共享资源的代码封装为方法,添加synchronized关键字修饰,这个方法就是同步方法,使用的锁是this对象。

    代码如下:

    1 public classDemo {2 public static voidmain(String[] args) {3 DemoThread dt = newDemoThread();4 Thread t1 = new Thread(dt, "窗口1");5 Thread t2 = new Thread(dt, "窗口2");6 t1.start();7 t2.start();8 }9 }10

    11 class DemoThread implementsRunnable {12 private int ticket = 3;13

    14 @Override15 public voidrun() {16 while (ticket > 0) {17 try{18 Thread.sleep(1);19 } catch(InterruptedException e) {20 e.printStackTrace();21 }22 sale();23 }24 }25

    26 public synchronized voidsale() {27 if (ticket > 0) {28 System.out.println(Thread.currentThread().getName() + " 进入卖票环节 ");29 System.out.println(Thread.currentThread().getName() + " 售卖的车票编号为: " + ticket--);30 }31 }32 }

    运行结果如下:

    1 窗口1 进入卖票环节2 窗口1 售卖的车票编号为: 3

    3 窗口2 进入卖票环节4 窗口2 售卖的车票编号为: 2

    5 窗口2 进入卖票环节6 窗口2 售卖的车票编号为: 1

    结果说明:

    在每次调用sale()方法售票的时候,程序会将实例对象this作为锁,保证一个时间只能有一个线程在操作共享资源。

    使用synchronized的静态同步方法

    如果该方法是静态方法,因为静态方法优先于类的实例化,所以静态方法是不能持有this的,静态同步方法的琐是类的class对象。

    代码如下:

    1 public classDemo {2 public static voidmain(String[] args) {3 DemoThread dt = newDemoThread();4 Thread t1 = new Thread(dt, "窗口1");5 Thread t2 = new Thread(dt, "窗口2");6 t1.start();7 t2.start();8 }9 }10

    11 class DemoThread implementsRunnable {12 private static int ticket = 3;13

    14 @Override15 public voidrun() {16 while (ticket > 0) {17 try{18 Thread.sleep(1);19 } catch(InterruptedException e) {20 e.printStackTrace();21 }22 sale();23 }24 }25

    26 public static synchronized voidsale() {27 if (ticket > 0) {28 System.out.println(Thread.currentThread().getName() + " 进入卖票环节 ");29 System.out.println(Thread.currentThread().getName() + " 售卖的车票编号为: " + ticket--);30 }31 }32 }

    运行结果如下:

    1 窗口2 进入卖票环节2 窗口2 售卖的车票编号为: 3

    3 窗口1 进入卖票环节4 窗口1 售卖的车票编号为: 2

    5 窗口2 进入卖票环节6 窗口2 售卖的车票编号为: 1

    结果说明:

    使用静态同步方法除了需要注意共享资源也要用static修饰外,其他的和普通同步方法是一样的。

    synchronized关键字和volatile关键字的区别

    含义

    volatile主要用在多个线程感知实例变量被更改了场合,从而使得各个线程获得最新的值。它强制线程每次从主内存中讲到变量,而不是从线程的私有内存中读取变量,从而保证了数据的可见性。

    synchronized主要通过对象锁控制线程对共享数据的访问,持有相同对象锁的线程只能等其他持有同一个对象锁的线程执行完毕之后,才能持有这个对象锁访问和处理共享数据。

    比较

    ◆ 量级比较

    volatile轻量级,只能修饰变量。

    synchronized重量级,还可修饰方法。

    ◆ 可见性和原子性

    volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。

    synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。

    同步使用总结

    要使用synchronized,必须要有两个以上的线程。单线程使用没有意义,还会使效率降低。

    要使用synchronized,线程之间需要发生同步,不需要同步的没必要使用synchronized,例如只读数据。

    使用synchronized的缺点是效率非常低,因为加锁、释放锁和释放锁后争抢CPU执行权的操作都很耗费资源。

    展开全文
  • 线程之间的通信:wait(),notify(),notifyAll()wait()——>让线程进入阻塞状态,暂停执行。一直阻塞notify()——>唤醒线程,wait()住的线程,被唤醒。如果多个线程wait()了,唤醒其中的一个。notifyAll()——...

    线程之间的通信:wait(),notify(),notifyAll()

    wait()——>让线程进入阻塞状态,暂停执行。一直阻塞

    notify()——>唤醒线程,wait()住的线程,被唤醒。如果多个线程wait()了,唤醒其中的一个。

    notifyAll()——>唤醒所有。

    语法要求:必须在同步中,由同步的锁对象来调用。否则java.lang.IllegalMonitorStateException异常。

    生产者消费者模型:

    生产者(线程t1)负责生产产品,存入容器中(固定容量),消费者(线程t2)从容器中获取产品消费掉。

    容器:

    生产者:持有资源,生产产品,存入容器中

    消费者:持有资源,消费掉产品。

    容器满了:最多装8个鸡蛋

    生产者:持有资源,暂停执行——直到容器还能继续装。

    锁对象.wait()——>会让线程进入阻塞状态。暂停执行。notify(),notifyAll()

    消费者:持有资源,直接消费。。

    容器空了:最少0个。

    生产者:持有资源,生产,存入。。

    消费者:持有资源,暂停执行——直到容器中有产品

    锁对象.wait()——>会让线程进入阻塞状态。暂停执行。notify(),notifyAll()

    e319e60e2459172c85fc83f5787ce80b.png

    1、产品类:Egg()-->id

    2、产生者:线程

    cpu执行,run()-->生产鸡蛋,装入容器

    3、消费者:线程

    cpu执行,run()-->从容器中获取鸡蛋,吃掉(打印。。)

    容器对象:

    Class实现容器

    数组: Egg[] arr = new Egg[8];

    集合:栈,后进先出

    同步的代码:

    锁的是容器对象:

    wait()和sleep()方法的区别:

    出处不同:

    sleep()方法是Thread类中定义的。

    wait()方法是Object类中定义的。

    解除阻塞的方式不同

    sleep()是时间到,自己醒。

    wait()方法等待被唤醒:notify(),或者是notifyAll()

    对锁资源的释放情况

    sleep(),不释放,抱着不撒手

    wait(),释放

    d9614c0b0bab97c8e4971179db35e5ef.png

    package com.qf.demo03;

    import java.util.LinkedList;

    //step1:产品类:Egg,ManTou,Computer,Bread。。。

    class Egg {

    private int id;

    public int getId() {

    return id;

    }

    public void setId(int id) {

    this.id = id;

    }

    public Egg() {

    super();

    }

    public Egg(int id) {

    super();

    this.id = id;

    }

    @Override

    public String toString() {

    return "Egg [id=" + id + "]";

    }

    }

    // step2:生产者线程:代表母鸡,下蛋

    class Producer implements Runnable {

    // step5:通过构造方法,获取容器

    private LinkedList list;

    private int id = 1;

    public Producer(LinkedList list) {

    this.list = list;

    }

    @Override

    public void run() {

    // 母鸡下蛋,装容器

    while (true) {

    //t1

    synchronized (list) {//t1

    while(list.size() == 8){

    System.out.println("容器满了,不能再装了,母鸡应该等待。。。");

    try {

    list.wait();//正在访问的list锁对象的线程,处于了等待状态,同时释放了锁对象。。

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    // 1.生产鸡蛋

    Egg egg = new Egg(id++);//1,2

    try {

    Thread.sleep((int) (Math.random() * 1000));

    //Thread.sleep(100);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    // 2.存入容器

    list.push(egg);//1个,2个

    System.out.println(Thread.currentThread().getName() + ",母鸡下蛋:" + egg.getId());

    list.notify();//唤醒消费者线程——>如果没有线程处于wait(),那么这就是空唤醒

    }

    }

    }

    }

    // step3:消费者线程:代表吃货,吃鸡蛋

    class Customer implements Runnable {

    // step5:通过构造方法,让Customer类,获取这个容器

    private LinkedList list;

    public Customer(LinkedList list) {

    this.list = list;

    }

    @Override

    public void run() {

    while (true) {

    //t2

    synchronized (list) {//this

    while(list.size() == 0){

    System.out.println("容器空的,不能吃了,吃货应该等待。。");

    try {

    list.wait();

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    // 消费鸡蛋

    Egg egg = list.pop();//8

    try {

    Thread.sleep((int) (Math.random() * 1000));

    //Thread.sleep(100);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    System.out.println("\t" + Thread.currentThread().getName() + ",消费了鸡蛋:" + egg.getId());// 8,7

    list.notify();//唤醒线程,唤醒刚刚因为list调用wait(),所处于等待的线程--->母鸡

    }

    }

    }

    }

    public class Test5ProducerAndCustomer {

    public static void main(String[] args) {

    /*

    * 模拟生产者和消费者

    */

    // step4:先创建一个容器

    LinkedList list = new LinkedList<>();

    // 传入到生产者和消费者:

    Producer p = new Producer(list);

    Customer c = new Customer(list);

    Thread t1 = new Thread(p, "母鸡");

    Thread t2 = new Thread(c, "吃货");

    t1.start();

    t2.start();

    }

    }

    展开全文
  • java的线程之间资源共享,所以会出现线程同步问题(即,线程安全)一、线程创建:方式①:extends java.lang.Thread,重写run(),run方法里是开启线程后要做事。.start()启动线程,执行run()里程序。方式②:...

    java的线程之间资源共享,所以会出现线程同步问题(即,线程安全)

    一、线程创建:

    方式①:extends java.lang.Thread,重写run(),run方法里是开启线程后要做的事。.start()启动线程,执行run()里的程序。

    方式②:implements java.lang.runnable,实现run(); 然后new Thread(implements产生的线程); 调用.start()启动线程

    Thread的方法:

    .run() 定义线程要做的事

    .start() 启动线程

    .sleep()方法使线程进入阻塞状态,当sleep结束后,线程进入就绪状态,继续执行接下来的程序。

    .wait() 线程等待

    .join() :当程序执行过程中调用了其他线程的join()方法时,则当前线程被阻塞,直到执行join()的线程 结束为止。也就是说join方法使一个线程等待另一个线程完成。

    .setDaemon(boolean): Daemon守护神。将此线程设置为守护线程。主线程死亡时,守护线程必须死亡,无论是否执行完。当守护线程比主线程执行的快时,可能会比主线程先死。

    .interrupt():友好的中断自己

    .stop():强制中断线程,不友好

    .notify():唤醒线程

    .interrupted():可清除线程的中断状态,即,将线程状态改为非中断

    .isInterrupted():线程的中断状态不受影响:返回线程的状态:是否中断。不会对线程的现有状态有影响。

    二、线程同步(线程安全)

    多个线程同时对同一个对象的实例变量进行操作时,会引起同步问题(和操作系统中的进程同步问题是一样的道理,同步问题:数据脏读、不可重复读、丢失更新等)。

    解决的方法就是加锁:方法①在java里很简单,给某个方法or某段代码or某个变量加上synchronized关键字,即加锁。

    方法②“同步代码块”:给整个方法加锁,那么,进入这个方法的程序都要等待上一个程序释放这个资源,等待的时间太长,效率就低。所以常用的是只给方法中的某段代码加锁。也就是“同步代码块”:写法:synchronized (this) {需要加锁的某段代码}

    方法③“同步锁”:java5之后使用ReentrantLock对象和方法加锁。如下:

    1 //方法3:同步锁

    2 public ReentrantLock reentrantLock=newReentrantLock();3

    4 public /*加锁方法1:synchronized*/ voidgetMoney(String name) {5 //加锁方法2:synchronized (this) {

    6

    7 reentrantLock.lock();8 try{9 if (money > 1500) {10 money -= 1500;11 System.out.println(name + "取款1500成功!");12 } else{13 System.out.println(name + "余额不足!");14 }15 } catch(Exception e) {16 e.printStackTrace();17 } finally{//finally的作用:即使出现异常了,也可以执行解锁的方法unlock()

    18 reentrantLock.unlock();19 }20

    21 //}

    22 }

    方法④:对于集合中线程不安全的类,比如ArrayList,Collections类提供了使之可以变得线程安全的方法。如下:

    1 List list=Collections.synchronizedList(new ArrayList());

    展开全文
  • 在开发过程中,不管是初学者还是有经验程序员,都要考虑到线程安全这个问题,其实synchronized和volatile在Java中起着很...多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。拿上篇博...
  • java线程之间的同步

    2014-09-03 09:41:00
    同一进程多个线程共享同一片存储空间,在带来方便同时也出现了访问冲突问题。Java提供了一种专门机制来解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。 在java应用程序中无非存在成员和方法,...
  • 1.1 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息, 目的是为了能够让线程之间相互发送信号。另外,线程通信还能够使得线程等待其它线程的信号, 更多细节可以参考线程之间的通信(thread signal) ...
  • java线程的同步概念

    2019-04-10 11:00:08
    java线程的同步概念是个很有意义的玩意。synchronize从英译过来是"是同时发生"。 但其真正的含义确实截然相反的。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时...
  • 你可以.你可以这样做:class MotorA {private static MotorA motor ...private static final java.util.concurrent.locks.Lock lock = new java.util.concurrent.locks.ReentrantLock();private MotorA() { }pub...
  • 当多个线程同时共享,同一个全局变量或静态变量,做写操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。 案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用...
  • JAVA线程之间实现同步+多线程并发同步解决方案

    万次阅读 多人点赞 2018-03-04 14:09:15
     当多个线程同时共享同一个全局变量或静态变量,做写操作(修改变量值)时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作时不会发生数据冲突问题。案例:需求现在有100张火车票,有两个窗口同时抢...
  • /*** @author xingsir* 线程提供了一个方法:void join() ,join可以协调线程之间的同步运行。* 此方法允许执行这个方法的线程在该方法所属线程后等待,直到该方法所属线程结束后方可继续运行,否则会一致处于 阻塞...
  • 互斥同步线程之间的协作互斥同步Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是 JDK 实现的 ReentrantLock。synchronized同步一个代码块public void ...
  • 转载自:这里学习了基础的线程知识 看到了 线程之间的通信线程之间有哪些通信方式呢?1、同步这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信。public classMyObject {publicMyObject() ...
  • 1. 多线程实现最核心机制 一个程序在其执行过程中, 可以产生多个线程, 形成多条执行...Java应用程序总是从主类main()方法开始执行,如果main()方法中创建了其它线程,在主线程和其它线程之间轮流切换执行,保...
  • Java线程4个状态之间的转换;并结合例子说明了两种创建线程的方法。 线程是指程序中能顺序执行的一个序列。一个线程只有一个入口点? 但可能有几个出口点? 不过,每个时刻的执行点总是只有一个。线程是...
  • 当多个线程同时共享,同一个全局变量或静态变量,做写操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。 案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用...
  • Java 多线程同步以及线程之间的协作

    千次阅读 2018-04-01 12:55:37
    Java 线程有下列五种状态: 1. 初始状态(New) 线程对象被创建后,就进入了初始状态, 此时线程会被分配必须系统资源, 并进行了初始化操作, 代表该线程有资格获取CPU时间片了; 2. 就绪状态(Runnable) 线程对象...
  • 当两个线程竞争同一资源时,如果对资源访问顺序敏感,就称存在竞态条件。导致竞态条件发生代码区称作临界区。 一个存在竞态条件例子: class Counter { protected long count; public Counter(){}; public ...
  • * join方法可以协调线程之间的同步运行 * join方法会让运行该方法的线程处于阻塞状态,直到 * 该方法所属线程运行完毕才会解除阻塞。 * @author Administrator * */ public class JoinDemo { //图片是否下载...
  • java线程(线程同步

    2017-01-26 11:55:18
    为何要使用同步?   java允许多线程并发控制,当... 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,   从而保证了该变量唯一性和准确性。
  • 3.特殊域变量violatile实现的同步 4.使用reetranlock实现的线程同步 5使用局部变量实现线程同步,如果使用threadloca管理变量,则每一个使用该变量的线程都获得该变量的一个副本,副本之间相互独立,这样每一个线程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,525
精华内容 2,210
关键字:

java线程之间的同步

java 订阅