Java多线程编程核心 - 对象及变量的并发访问

1、什么是“线程安全”与“非线程安全”?

“非线程安全”会在多个线程对同一对象总的实例变量进行并发访问时发生,产生的后果是“脏读”,也就是取到的数据其实是被更改过的。

“线程安全”是以获得的实例变量的值是经过同步处理的,不会出现脏读的现象。

2、非线程安全例子?怎么解决?


非线程安全


package com.jvm.thread;

public class HasSelfPrivateNum {

private int num =  0;

public void add(String username){

try{

if(username.equals("a")){

num = 100;

System.out.println("a set over!");

Thread.sleep(2000);

}else{

num = 200;

System.out.println("b set over!");

}

System.out.println(username + " num = " + num);

}catch(InterruptedException e){

e.printStackTrace();

}

}

}


package com.jvm.thread;

public class MyThreadA extends Thread {

private HasSelfPrivateNum obj;

public MyThreadA(HasSelfPrivateNum obj){

this.obj = obj;

}

@Override

public void run() {

super.run();

obj.add("a");

}

}


package com.jvm.thread;

public class MyThreadB extends Thread {

private HasSelfPrivateNum obj;

public MyThreadB(HasSelfPrivateNum obj){

this.obj = obj;

}

@Override

public void run() {

super.run();

obj.add("b");

}

}


package com.jvm.thread;

public class MyThread06 {

public static void main(String[] args) {

HasSelfPrivateNum obj = new HasSelfPrivateNum();

MyThreadA a = new MyThreadA(obj);

a.start();

MyThreadB b = new MyThreadB(obj);

b.start();

}

}


a set over!

b set over!

b num = 200

a num = 200


解决方法:在 add() 前加关键字synchronized


a set over!

a num = 100

b set over!

b num = 200

3、synchronized 关键字

当A线程调用anyObject 对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,更准确地讲,获得的是对象的锁,所以,其他线程必须等待A线程执行完毕才能调用X方法,但线程B可以随意调用其他的非synchronized同步方法。

当A线程调用anyObject 对象加入synchronized关键字的X方法时,A线程就获得了X方法所在的对象的锁,所以其他线程必须等待A线程执行完毕才可以调用X方法,而B如果调用声明了synchronized关键字的非X方法时,必须等待A线程将X方法执行完,也就是释放对象锁后才可以调用。

4、synchronized锁重入

“可重入锁”:自己可以再次获得自己的内部锁。可重入锁也支持在父子继承的环境中。


package com.jvm.thread;

public class Service extends Thread {

synchronized public void service1(){

System.out.println("service1");

service2();

}

synchronized public void service2(){

System.out.println("service2");

service3();

}

synchronized public void service3(){

System.out.println("service3");

}

@Override

public void run() {

super.run();

Service service = new Service();

service.service1();

}

public static void main(String[] args) {

Service service = new Service();

service.start();

}

}


service1

service2

service3

5、锁自动释放——出现异常

当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

6、synchronized同步方法的缺点?解决办法?

Synchronized同步方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长时间。

在这样的情况下可以使用synchronized同步语句块来解决。

7、synchronized同步语句块的使用?

当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另外一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。


package com.jvm.thread;

public class MyTask {

public void taskMethod(){

try {

synchronized (this) {

System.out.println("begin time=" + System.currentTimeMillis());

Thread.sleep(2000);

System.out.println("end time=" + System.currentTimeMillis());

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}


package com.jvm.thread;

public class MyThreadA extends Thread {

private MyTask task;

public MyThreadA(MyTask task){

this.task = task;

}

@Override

public void run() {

task.taskMethod();

}

}


package com.jvm.thread;

public class MyThreadB extends Thread {

private MyTask task;

public MyThreadB(MyTask task){

this.task = task;

}

@Override

public void run() {

task.taskMethod();

}

}


package com.jvm.thread;

public class Run {

public static void main(String[] args) {

MyTask task = new MyTask();

MyThreadA a = new MyThreadA(task);

a.setName("a");

a.start();

MyThreadB b = new MyThreadB(task);

b.setName("b");

b.start();

}

}


begin time=1498358631251

end time=1498358633252

begin time=1498358633252

end time=1498358635252

8、synchronized代码块间的同步性?

在使用不同synchronized(this)代码块时需要注意的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的“对象监视器”是一个。

9、将任意对象作为对象监视器?优点?

使用synchronized(this)格式来同步代码块,其实Java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。

优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但是会受到阻塞,所以影响运行效率;但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,则可大大提高效率。


package com.jvm.thread;

public class Service extends Thread {

private String username;

private String password;

private String anyString = new String();

public void setUsernamePassword(String username, String pwssword){

try {

synchronized (anyString) {

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in to synchronized block");

username = username;

Thread.sleep(3000);

pwssword = pwssword;

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave synchronized block");

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}


package com.jvm.thread;

public class MyThreadA extends Thread {

private Service service;

public MyThreadA(Service service){

this.service = service;

}

@Override

public void run() {

service.setUsernamePassword("a", "aa");

}

}


package com.jvm.thread;

public class MyThreadB extends Thread {

private Service service;

public MyThreadB(Service service){

this.service = service;

}

@Override

public void run() {

service.setUsernamePassword("b", "bb");

}

}


package com.jvm.thread;

public class Run {

public static void main(String[] args) {

Service service = new Service();

MyThreadA a = new MyThreadA(service);

a.setName("A");

a.start();

MyThreadB b = new MyThreadB(service);

b.setName("B");

b.start();

}

}


Thread name:A at 1498744309904 go in to synchronized block

Thread name:A at 1498744312906 leave synchronized block

Thread name:B at 1498744312906 go in to synchronized block

Thread name:B at 1498744315906 leave synchronized block

10、3个结论

“synchronized(非this对象x)”格式的写法是将x对象本身作为“对象监视器”,这样就可以得出以下3个结论:

1)当多个线程同时执行synchronized(x){}同步代码块时呈同步效果。

2)当其他线程执行x对象中synchronized同步方法时呈同步效果。

3)当其他线程执行x对象方法里面的synchronized(this)代码块时也呈同步效果。

原因:使用同一个“对象监视器”。

11、静态同步synchronized方法与synchronized(class)代码块

关键字synchronized还可以应用在static静态方法上,如果这样写,那是对当前的 *.java文件对应的Class类进行持锁。


package com.jvm.thread;

public class Service extends Thread {

synchronized public static void printA() {

try {

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printA()");

Thread.sleep(3000);

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printA()");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

synchronized public static void printB() {

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printB()");

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printB()");

}

}


package com.jvm.thread;

public class MyThreadA extends Thread {

private Service service;

public MyThreadA(Service service){

this.service = service;

}

@Override

public void run() {

service.printA();

}

}


package com.jvm.thread;

public class MyThreadB extends Thread {

private Service service;

public MyThreadB(Service service){

this.service = service;

}

@Override

public void run() {

service.printB();

}

}


package com.jvm.thread;

public class Run {

public static void main(String[] args) {

Service service = new Service();

MyThreadA a = new MyThreadA(service);

a.setName("A");

a.start();

MyThreadB b = new MyThreadB(service);

b.setName("B");

b.start();

}

}


Thread name:A at 1498746369790 go in printA()

Thread name:A at 1498746372791 leave printA()

Thread name:B at 1498746372792 go in printB()

Thread name:B at 1498746372792 leave printB()


分析:从运行结果来看,和synchronized加到非static方法上使用效果一样。其实有本质上的不同,synchronized加到static静态方法上是给Class类加锁,而synchronized加到非static方法上是给对象加锁。


package com.jvm.thread;

public class Service extends Thread {

synchronized public static void printA() {

try {

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printA()");

Thread.sleep(3000);

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printA()");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

synchronized public static void printB() {

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printB()");

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printB()");

}

synchronized public void printC() {

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printC()");

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printC()");

}

}


package com.jvm.thread;

public class MyThreadC extends Thread {

private Service service;

public MyThreadC(Service service){

this.service = service;

}

@Override

public void run() {

service.printC();

}

}


package com.jvm.thread;

public class Run {

public static void main(String[] args) {

Service service = new Service();

MyThreadA a = new MyThreadA(service);

a.setName("A");

a.start();

MyThreadB b = new MyThreadB(service);

b.setName("B");

b.start();

MyThreadC myThreadC = new MyThreadC(service);

myThreadC.setName("C");

myThreadC.start();

}

}

 


Thread name:A at 1498746943314 go in printA()

Thread name:C at 1498746943315 go in printC()

Thread name:C at 1498746943315 leave printC()

Thread name:A at 1498746946315 leave printA()

Thread name:B at 1498746946315 go in printB()

Thread name:B at 1498746946315 leave printB()

 


分析:异步的原因是持有不同的锁,一个是对象锁,另外一个是Class锁,而Class锁可以对类的所有对象实例起作用。


package com.jvm.thread;

public class Service extends Thread {

synchronized public static void printA() {

try {

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printA()");

Thread.sleep(3000);

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printA()");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

synchronized public static void printB() {

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printB()");

System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printB()");

}

}


package com.jvm.thread;

public class Run {

public static void main(String[] args) {

Service service1 = new Service();

Service service2 = new Service();

MyThreadA a = new MyThreadA(service1);

a.setName("A");

a.start();

MyThreadB b = new MyThreadB(service2);

b.setName("B");

b.start();

}

}


Thread name:A at 1498747202057 go in printA()

Thread name:A at 1498747205057 leave printA()

Thread name:B at 1498747205057 go in printB()

Thread name:B at 1498747205057 leave printB()

同步代码块synchronized(class)代码块的作用和synchronized static方法的作用一样。

12、数据类型String的常量池特性


在JVM中具有String常量池缓冲的功能


package com.jvm.thread;

public class Test {

public static void main(String[] args) {

String a = "a";

String b = "a";

System.out.println(a == b); //true

}

}

将synchronized(string)同步块与String联合使用时,要注意常量池带来的一些例外。


package com.jvm.thread;

public class Service extends Thread {

public static void print(String str) {

try {

synchronized (str) {

while(true){

System.out.println(Thread.currentThread().getName());

Thread.sleep(1000);

}

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}


package com.jvm.thread;

public class MyThreadA extends Thread {

private Service service;

public MyThreadA(Service service){

this.service = service;

}

@Override

public void run() {

service.print("AA");

}

}


package com.jvm.thread;

public class MyThreadB extends Thread {

private Service service;

public MyThreadB(Service service){

this.service = service;

}

@Override

public void run() {

service.print("AA");

}

}


package com.jvm.thread;

public class Run {

public static void main(String[] args) {

Service service = new Service();

MyThreadA a = new MyThreadA(service);

a.setName("A");

a.start();

MyThreadB b = new MyThreadB(service);

b.setName("B");

b.start();

}

}


A

A

A

A

A

A

A


分析:死循环,原因是两个值都是AA两个线程持有相同的锁,所以造成线程B不能执行。这就是String常量池所带来的问题。因此,在大多数情况下,同步synchronized代码块都不适用String作为锁对象,而改用其他,比如 new Object()实例化一个Oject对象。

13、同步synchronized方法无限等待与解决


package com.jvm.thread;

public class Service extends Thread {

synchronized public void methodA() {

System.out.println("methodA begin");

boolean isContinueRun = true;

while (isContinueRun) {

}

System.out.println("methodA end");

}

synchronized public void methodB() {

System.out.println("methodB begin");

System.out.println("methodB end");

}

}


package com.jvm.thread;

public class MyThreadA extends Thread {

private Service service;

public MyThreadA(Service service){

this.service = service;

}

@Override

public void run() {

service.methodA();

}

}


package com.jvm.thread;

public class MyThreadB extends Thread {

private Service service;

public MyThreadB(Service service){

this.service = service;

}

@Override

public void run() {

service.methodB();

}

}


package com.jvm.thread;

public class Run {

public static void main(String[] args) {

Service service = new Service();

MyThreadA a = new MyThreadA(service);

a.start();

MyThreadB b = new MyThreadB(service);

b.start();

}

}


methodA begin


分析:线程A不释放锁,线程B永远得不到运行的机会,锁死了。


解决:同步代码块


package com.jvm.thread;

public class Service extends Thread {

Object object1 = new Object();

public void methodA() {

synchronized (object1) {

System.out.println("methodA begin");

boolean isContinueRun = true;

while (isContinueRun) {

}

System.out.println("methodA end");

}

}

Object object2 = new Object();

public void methodB() {

synchronized (object2) {

System.out.println("methodB begin");

System.out.println("methodB end");

}

}

}


methodA begin

methodB begin

methodB end

14、多线程的死锁

Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能释放的锁,从而导致所有的任务都无法继续完成。在多线程技术中心,“死锁”是必需避免的,因为这会造成线程的“假死”。


死锁例子:


package com.jvm.thread;

public class DeadThread implements Runnable {

public String username;

public Object lock1 = new Object();

public Object lock2 = new Object();

public void setFlag(String username){

this.username = username;

}

@Override

public void run() {

if(username.equals("a")){

synchronized (lock1) {

try {

System.out.println("username = " + username);

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (lock2) {

System.out.println("lock1 -> lock2");

}

}

}

if(username.equals("b")){

synchronized (lock2) {

try {

System.out.println("username = " + username);

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (lock1) {

System.out.println("lock2 -> lock1");

}

}

}

}

public static void main(String[] args) throws InterruptedException {

DeadThread deadThread = new DeadThread();

deadThread.setFlag("a");

Thread thread1 = new Thread(deadThread);

thread1.start();

Thread.sleep(1000);

deadThread.setFlag("b");

Thread thread2 = new Thread(deadThread);

thread2.start();

}

}


username = a

username = b


注意:死锁的实现与嵌套不嵌套没有关系。

1/ valotile 关键字的作用是什么?缺点是什么?

使变量在多个线程间可见。但valotile关键字最致命的缺点是不支持原子性。


package com.jvm.thread;

public class PrintString {

private boolean isContinuePrint = true;

public void setContinuePrint(boolean isContinuePrint) {

this.isContinuePrint = isContinuePrint;

}

public boolean isContinuePrint() {

return isContinuePrint;

}

public void printStringMethod(){

try {

while (isContinuePrint) {

System.out.println("run printStringMethod threadName=" + Thread.currentThread().getName());

Thread.sleep(1000);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

public static void main(String[] args) {

PrintString printString = new PrintString();

printString.printStringMethod();

System.out.println("I will stop it! stopThread=" + Thread.currentThread().getName());

printString.setContinuePrint(false);

}

}


run printStringMethod threadName=main

run printStringMethod threadName=main

run printStringMethod threadName=main

run printStringMethod threadName=main

run printStringMethod threadName=main

run printStringMethod threadName=main

run printStringMethod threadName=main

run printStringMethod threadName=main

run printStringMethod threadName=main

run printStringMethod threadName=main


分析:main线程一直在处理while循环,没办法执行后面的代码。

解决:使用多线程


package com.jvm.thread;

public class PrintString implements Runnable {

private boolean isContinuePrint = true;

public void setContinuePrint(boolean isContinuePrint) {

this.isContinuePrint = isContinuePrint;

}

public boolean isContinuePrint() {

return isContinuePrint;

}

public void printStringMethod(){

try {

while (isContinuePrint) {

System.out.println("run printStringMethod threadName=" + Thread.currentThread().getName());

Thread.sleep(1000);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

@Override

public void run() {

printStringMethod();

}

public static void main(String[] args) {

PrintString printString = new PrintString();

new Thread(printString).start();

System.out.println("I will stop it! stopThread=" + Thread.currentThread().getName());

printString.setContinuePrint(false);

}

}


I will stop it! stopThread=main

run printStringMethod threadName=Thread-0

 


package com.jvm.thread;

public class PrintString implements Runnable {

volatile private boolean isContinuePrint = true;

public void setContinuePrint(boolean isContinuePrint) {

this.isContinuePrint = isContinuePrint;

}

public boolean isContinuePrint() {

return isContinuePrint;

}

public void printStringMethod(){

try {

while (isContinuePrint) {

System.out.println("run printStringMethod threadName=" + Thread.currentThread().getName());

Thread.sleep(1000);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

@Override

public void run() {

printStringMethod();

}

public static void main(String[] args) {

PrintString printString = new PrintString();

new Thread(printString).start();

System.out.println("I will stop it! stopThread=" + Thread.currentThread().getName());

printString.setContinuePrint(false);

}

}


I will stop it! stopThread=main

run printStringMethod threadName=Thread-0


使用volatile关键字,强制从公共内存中读取变量的值。

2/ 线程安全包含哪些方面?

原子性和可见性。Java的同步机制都是围绕这两个方面来确保线程安全的。

3/关键字synchronized和valotile比较?

a/关键字valotile是线程同步的轻量级实现,因此valotile性能更好。Valotile只能修饰变量,synchronized修饰方法和代码块。

b/多线程访问valotile不会发生阻塞,而synchronized会。

c/valotile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。

d/valotile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问资源的同步性。

3/valotile非原子性?怎么解决?


package com.jvm.thread;

public class MyThread extends Thread {

volatile public static int count;

private static void addCount(){

for(int i = 0; i < 100; i++){

count++;

}

System.out.println("count=" + count);

}

@Override

public void run() {

addCount();

}

public static void main(String[] args) {

MyThread[] myThreadArr = new MyThread[100];

for(int i = 0; i < 100; i++){

myThreadArr[i] = new MyThread();

}

for(int i = 0; i < 100; i++){

myThreadArr[i].start();

}

}

}


count=100

count=300

count=200

count=400

count=600

count=600

count=700

count=800

count=900

count=1000

count=1100

count=1200

count=1400

count=1300

count=1500

count=1600

count=1700

count=1800

count=2000

count=1900

count=2200

count=2300

count=2200

count=2400

count=2500

count=2600

count=2900

count=2800

count=2700

count=3000

count=3100

count=3200

count=3300

count=3400

count=3600

count=3700

count=3500

count=4100

count=4000

count=3900

count=4300

count=3800

count=4400

count=4600

count=4200

count=4500

count=4800

count=4700

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6605

count=6705

count=6805

count=7005

count=7005

count=7205

count=7305

count=7405

count=7105

count=7505

count=7605

count=7705

count=7805

count=7905

count=8005

count=8105

count=8205

count=8305

count=8405

count=8505

count=8605

count=8705

count=8805

count=8905

count=9005

count=9105

count=9205

count=9305

count=9405

count=9505

count=9605

count=9705

count=9805

count=9905


分析:用图来演示使用关键字valotile时出现非线程安全的原因。

a/ read和load阶段:从主内存复制变量到当前线程工作内存;

b/ use和assign阶段:执行代码,改变共享变量值;

c/ store和write阶段:用工作内存数据刷新主存对应变量的值。

在多线程环境中,use和assign是多次出现的,但这一操作并不是原子性,也就是在read和assign之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,导致私有内存和公共内存中的变量不同步,因此,计算出来的结果和预期不一样,也就出现了非线程安全问题。


解决:valotile关键字解决的是变量读取时的可见性问题,但无法保证原子性,因此,对于多个线程访问同一实例变量还是需要加锁同步。


package com.jvm.thread;

public class MyThread extends Thread {

public static int count;

private synchronized static void addCount(){

for(int i = 0; i < 100; i++){

count++;

}

System.out.println("count=" + count);

}

@Override

public void run() {

addCount();

}

public static void main(String[] args) {

MyThread[] myThreadArr = new MyThread[100];

for(int i = 0; i < 100; i++){

myThreadArr[i] = new MyThread();

}

for(int i = 0; i < 100; i++){

myThreadArr[i].start();

}

}

}


count=100

count=200

count=300

count=400

count=500

count=600

count=700

count=800

count=900

count=1000

count=1100

count=1200

count=1300

count=1400

count=1500

count=1600

count=1700

count=1800

count=1900

count=2000

count=2100

count=2200

count=2300

count=2400

count=2500

count=2600

count=2700

count=2800

count=2900

count=3000

count=3100

count=3200

count=3300

count=3400

count=3500

count=3600

count=3700

count=3800

count=3900

count=4000

count=4100

count=4200

count=4300

count=4400

count=4500

count=4600

count=4700

count=4800

count=4900

count=5000

count=5100

count=5200

count=5300

count=5400

count=5500

count=5600

count=5700

count=5800

count=5900

count=6000

count=6100

count=6200

count=6300

count=6400

count=6500

count=6600

count=6700

count=6800

count=6900

count=7000

count=7100

count=7200

count=7300

count=7400

count=7500

count=7600

count=7700

count=7800

count=7900

count=8000

count=8100

count=8200

count=8300

count=8400

count=8500

count=8600

count=8700

count=8800

count=8900

count=9000

count=9100

count=9200

count=9300

count=9400

count=9500

count=9600

count=9700

count=9800

count=9900

count=10000

4/验证synchronized具有将线程工作内存的私有变量与公共内存中的变量同步的功能?

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或者代码快。它包含两个特性:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都能看到由同一个锁保护之前所有的修改效果。


package com.jvm.thread;

public class Service {

private boolean isCoutinueRun = true;

public void runMethod(){

while(isCoutinueRun){

}

System.out.println("have stoped!");

}

public void stopMethod(){

isCoutinueRun = false;

}

}


package com.jvm.thread;

public class ThreadA extends Thread{

private Service service;

public ThreadA(Service service) {

this.service = service;

}

@Override

public void run() {

service.runMethod();

}

}


package com.jvm.thread;

public class ThreadB extends Thread{

private Service service;

public ThreadB(Service service) {

this.service = service;

}

@Override

public void run() {

service.stopMethod();

}

}


package com.jvm.thread;

public class Run {

public static void main(String[] args) throws InterruptedException {

Service service = new Service();

ThreadA threadA = new ThreadA(service);

threadA.start();

Thread.sleep(1000);

ThreadB threadB = new ThreadB(service);

threadB.start();

System.out.println("have start stop commad");

}

}


have start stop commad


分析:出现死循环,各线程间的数据值没有可视性造成的


解决:synchronized可以具有可视性


package com.jvm.thread;

public class Service {

private boolean isCoutinueRun = true;

public void runMethod(){

String anyString = new String();

while(isCoutinueRun){

synchronized (anyString) {

}

}

System.out.println("have stoped!");

}

public void stopMethod(){

isCoutinueRun = false;

}

}

5/总结?

着重“外练互斥,内修可见”,是掌握多线程并发的重要技术。

时间: 2024-11-07 11:16:52

Java多线程编程核心 - 对象及变量的并发访问的相关文章

java多线程(对象和变量的并发访问)

在现实开发中,我们写的线程肯定会有不同的实例在执行,此时就可能会出现"非线程安全问题",非线程安全就是:多个线程对同一个对象中的实例变量进行并发访问时候,有可能A和B线程同时读取到数据,先后进行更改,此时,该变量就不是我们期望的数据,也就是通常所说的"脏数据" 实例变量非线程安全 需要注意的是,方法中的变量是不存在非线程安全问题的,这是因为方法内部的变量都是私有的. 如果多个线程共同访问了一个对象中的实例变量,则可能会出现线程安全问题.看下面代码: public c

第二章:对象及变量的并发访问

为什么要使用多线程编程?什么时候会出现线程安全问题? 在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的的资源:一个变量.一个对象.一个文件.一个数据库表等,而当多个线程同时访问同一个资源的时候,就会存在一个问题: 由于每个线程执行的过程是不可控的,所以很可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错. 举个简单的例子: 现在有两个线程分别从网络上读取数据,然后插入一张数据库表中,要求不能插入重复的数据. 那么必然在插入数

(1)Java多线程编程核心——Java多线程技能

1.为什么要使用多线程?多线程的优点? 提高CPU的利用率 2.什么是多线程? 3.Java实现多线程编程的两种方式? a.继承Thread类 public class MyThread01 extends Thread {     @Override     public void run() {         super.run();         System.out.println("MyThread01");     } public static void main(S

(二)对象以及变量的并发访问--synchronized的使用细节,用法

具体的记录synchronized关键的各种使用方式,注意事项.感觉一步一步跟我来都可以看懂滴 大致是按照以下思路进行书写的.黑体字可以理解为结论, 1.synchronized锁的是什么? 2.synchronized能够锁住所有方法吗? 3.synchronized能够用来锁住一个方法之中的部分代码吗? 4.synchronized能够锁住除了this以外的其他对象吗?有什么用?有什么需要注意的? -----------------------------------------------

Java多线程编程(二)对象及变量的并发访问

一.synchronized同步方法 1.方法内的变量为线程安全 2.实例变量非线程安全 3.多个对象多个锁 4.synchronized方法与锁对象 5.脏读 6.synchronized锁冲入 7.出现异常,锁自动释放 8.同步不具有继承性 二.synchronized同步语句块 1.synchronized方法的弊端 2.synchronized同步代码块的使用 3.用同步代码块解决同步的弊端 4.一半异步,一半同步 5.synchronized代码块间的同步性 6.验证同步synchro

多线程--对象及变量的并发访问

1 . 多个线程访问多个对象JVM会创建多个锁.2 . 静态方法是以类为单位进行同步的--对于同一个类中的所有静态方法,在同一时间内,只允许有一个线程执行其中的一个静态方法,其余想要进入这些方法的线程都必须挂起等待.非静态方法是以对象为单位进行同步的.3 .假设现有两个线程A和B,一个object对象,当线程A调用object对象的一个同步方法M1时,线程A就获得了M1方法所在对象的锁,所以其他线程必须等待线程A执行完毕之后才能调用方法M1,如果线程B调用object的同步方法M2,必须等待线程

第二章 对象以及变量的并发访问

synchronied 对象监视器为Object时的使用,或者监视器为Class时的使用. 方法中的变量不存在非线程安全问题,永远都是线程安全的,这是方法内部的变量是私有的特性造成的. 1 synchronized的使用 在方法前加关键字synchronized即可. 1)A线程先持有object对象的Lock锁,B线程可以异步的方式调用object对象中的非synchrionized类型的方法. 2)A线程先持有object对象的Lock锁,B线程如果在这时调用了object对象中的synch

对象及变量的并发访问一

一.多个线程操作一个对象实例 当两个线程同时访问一个没有同步的方法,如果两个线程同时操作业务对象中的实例变量,则有可能会出现“非线程安全问题”. 1 package concurrent; 2 /** 3 * 测试不同线程操作同一个实例变量线程安全问题 4 * @author foolishbird_lmy 5 * 6 */ 7 class ThePrivateNumber{ 8 private int num = 0; 9 10 public synchronized void addI(St

《Java 多线程编程核心技术》- 笔记

作为业务开发人员,能够在工作中用到的技术其实不多.虽然平时老是说什么,多线程,并发,注入,攻击!但是在实际工作中,这些东西不见得用得上.因为,我们用的框架已经把这些事做掉了. 比如web开发,外面有大量的请求进来,按理说,我们应该考虑并发问题.但其实,spring接到请求,分配到controller之后,就已经是线程安全的了,所以我们要做的就是,从controller开始,到最后请求响应结束,保证线程安全即可. 多线程好像有很多东西需要注意,阅读<Java 多线程编程核心技术>后,做个总结,总