继承Thread类实现多线程

public class MyThread extends Thread {

    @Override
public void run() {
super.run();
System.out.println("MyThread...");
} public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("运行结束..");
}
}

运行结果如下:

运行结束..
MyThread...

实现Runnable接口实现多线程

如果创建的线程类已经有一个父类了,就不能再集成Thread类,因为Java不支持多继承,这时就需要实现Runnable接口。

public class MyRunnable implements Runnable {

    @Override
public void run() {
System.out.println("MyRunnable is running...");
} public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
System.out.println("运行结束..");
}
}

运行结果如下:

运行结束..
MyRunnable is running...

实例变量与线程安全

  1. 不共享数据的情况
public class MyThread3 extends Thread {
private int count = 5;
public MyThread3(String name){
super();
this.setName(name);
} @Override
public void run() {
super.run();
while (count > 0) {
count--;
System.out.println("执行者:" + this.currentThread().getName() + ",count=" + count);
}
} public static void main(String[] args) {
MyThread3 t1 = new MyThread3("A");
MyThread3 t2 = new MyThread3("B");
MyThread3 t3 = new MyThread3("C");
t1.start();
t2.start();
t3.start();
}
}

某一次运行结果如下:

执行者:B,count=4
执行者:B,count=3
执行者:B,count=2
执行者:C,count=4
执行者:A,count=4
执行者:A,count=3
执行者:C,count=3
执行者:C,count=2
执行者:B,count=1
执行者:B,count=0
执行者:C,count=1
执行者:A,count=2
执行者:A,count=1
执行者:A,count=0
执行者:C,count=0
  1. 共享数据的情况
public class MyThread4 extends Thread {
private int count = 5; @Override
public void run() {
super.run();
count--;
System.out.println("执行者:" + Thread.currentThread().getName() + ",count=" + count);
} public static void main(String[] args) {
MyThread4 myThread = new MyThread4();
Thread t1 = new Thread(myThread, "A");
Thread t2 = new Thread(myThread, "B");
Thread t3 = new Thread(myThread, "C");
Thread t4 = new Thread(myThread, "D");
Thread t5 = new Thread(myThread, "E");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}

某一次的运行结果如下:

执行者:A,count=3
执行者:D,count=1
执行者:E,count=2
执行者:B,count=3
执行者:C,count=0

i--分成如下三步:

取得原有的i值

计算i-1

对i进行赋值


synchronized同步方法

方法中的变量不存在非线程安全问题,永远都是线程安全的。这是方法内部的变量是私有的特性造成的。

如果多个线程共同访问一个对象中的实例变量,则有可能出现“非线程安全”问题。用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉的情况。

package com.umgsai.thread.thread73;

多个对象多个锁

public class Run {
public static void main(String[] args) {
Service service1 = new Service();
Service service2 = new Service();
ThreadA threadA = new ThreadA(service1);
threadA.start();
ThreadB threadB = new ThreadB(service2);
threadB.start();
}
}

上面的代码是两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却是以异步方式运行的。synchronized关键字取得的锁是对象锁,而不是把一段代码或方法当做锁,所以在以上的代码中,哪个线程先执行带synchronized关键字的方法,哪个线程就持有改方法所属对象的锁Lock,那么其他线程只能处于等待状态,前提是多个线程访问的是同一个对象。如果多个线程访问多个对象,则JVM会创建多个锁。


synchronized方法的弊端

同步方法里的长任务。

package com.umgsai.thread.thread74;

当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块。

package com.umgsai.thread.thread74;

synchronized同步方法

  1. 对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。
  2. 同一时间只有一个线程可以执行synchronized同步方法中的代码。

synchronized(this)同步代码块

  1. 对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。
  2. 同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码。

数据类型String的常量池特性

package com.umgsai.thread.thread76;

出现这种情况就是因为Sting的两个值都是AA,两个线程持有相同的锁,所以造成线程B不能执行。因此在大多数情况下,同步synchronized代码块都不实用String作为锁对象,而改用其他,比如new Object()实例化一个Object对象,但他并不放入缓存中。


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

public class Service {
synchronized public void methodA(){
System.out.println("methodA begin...");
boolean condition = true;
while (condition) { }
System.out.println("methodA end...");
} synchronized public void methodB(){
System.out.println("methodB begin...");
System.out.println("methodB end...");
}
}
public class ThreadA extends Thread {
private Service service; public ThreadA(Service service) {
super();
this.service = service;
} @Override
public void run() {
service.methodA();
}
} public class ThreadB extends Thread {
private Service service; public ThreadB(Service service) {
super();
this.service = service;
} @Override
public void run() {
service.methodB();
}
}
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
ThreadB b = new ThreadB(service);
b.setName("B");
a.start();
b.start();
}
}

控制台打印结果如下:

methodA begin...

线程A处于死循环状态,线程B永远无法拿到Service对象锁而一直得不到运行。

对Service对象做如下修改:

public class Service {
Object object1 = new Object();
Object object2 = new Object();
public void methodA() {
synchronized (object1) {
System.out.println("methodA begin...");
boolean condition = true;
while (condition) { }
System.out.println("methodA end...");
}
} public void methodB() {
synchronized (object2) {
System.out.println("methodB begin...");
System.out.println("methodB end...");
}
}
}

此时控制台打印结果如下:

methodA begin...
methodB begin...
methodB end...

methodA()和methodB()对不同的对象加锁,所以线程A持有的锁不会对线程B造成影响。

多线程死锁

package com.umgsai.thread.thread17

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 (Exception 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 (Exception e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("按lock2->lock1代码顺序执行了");
}
}
}
} public static void main(String[] args) {
try {
DeadThread t1 = new DeadThread();
t1.setFlag("a");
Thread thread1 = new Thread(t1);
thread1.start();
Thread.sleep(200); t1.setFlag("b");
Thread thread2 = new Thread(t1);
thread2.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}

可以使用jps命令查看当前线程的id,然后使用jstack -l id来检查是否存在死锁。


对象锁的改变

package com.umgsai.thread.thread21;

public class MyService {
private String lock = "123";
public void testMethod() {
try {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis());
lock = "456";
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis());
}
} catch (Exception e) {
e.printStackTrace();
}
}
} public class ThreadA extends Thread {
private MyService service; public ThreadA(MyService service) {
super();
this.service = service;
} @Override
public void run() {
service.testMethod();
}
} public class ThreadB extends Thread {
private MyService service; public ThreadB(MyService service) {
super();
this.service = service;
} @Override
public void run() {
service.testMethod();
}
} public class Run1 {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
ThreadB b = new ThreadB(service);
b.setName("B");
a.start();
Thread.sleep(100);
b.start();
}
}

关键字volatile与死循环

package com.umgsai.thread.thread22

public class PrintString {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStringMethod() {
try {
while (isContinuePrint) {
System.out.println("printStringMethod is running...threadName=" + Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
} public static void main(String[] args) {
PrintString printString = new PrintString();
printString.printStringMethod();//同步执行,死循环
System.out.println("停止线程...");
printString.setContinuePrint(false);
}
}

以上同步代码出现死循环,无法停止

public class NewPrintString implements Runnable {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStringMethod() {
try {
while (isContinuePrint) {
System.out.println("printStringMethod is running...threadName=" + Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
printStringMethod();
} public static void main(String[] args) throws InterruptedException {
NewPrintString printString = new NewPrintString();
new Thread(printString).start();
Thread.sleep(5000);
System.out.println("停止线程...");
printString.setContinuePrint(false);
}
}

注:《Java多线程编程核心技术》P120讲将上面的代码运行在-server服务器模式中的64bit的JVM上时,会出现死循环。实际测试并未出现死循环,暂未弄清原因。

解决可能出现的死循环

public class RunThread extends Thread {
volatile private boolean isRunning = true;
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
} @Override
public void run() {
System.out.println("进入run方法...");
while (isRunning) {
System.out.println("running....");
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("线程被停止...");
} public static void main(String[] args) {
try {
RunThread runThread = new RunThread();
runThread.start();
Thread.sleep(1000);
runThread.setRunning(false);
System.out.println("已将isRunning设置为false");
} catch (Exception e) {
e.printStackTrace();
}
}
}

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

使用volatile关键字增加了实例变量在多个线程之间的可见性,但是volatile关键字不支持原子性。

synchronized和volatile的比较

  1. 关键字volatile是线程同步的轻量级实现,性能比synchronized好。volatile只能修饰变量,synchronized可以修饰方法和代码块。
  2. 多线程访问volatile不会发生阻塞,synchronized会出现阻塞。
  3. volatile能保证数据的可见性,但不能保证原子性。synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。
  4. 关键字volatile解决的是变量在多个线程之间的可见性,synchronized解决的是多个线程之间访问资源的同步性。

volatile的非原子性

public class VolatileTest 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) {
VolatileTest[] volatileTests = new VolatileTest[100];
for (int i = 0; i < 100; i++) {
volatileTests[i] = new VolatileTest();
}
for (int i = 0; i < 100; i++) {
volatileTests[i].start();
}
}
}

控制台打印结果如下:

.......
count=5332
count=5232
count=5132
count=5032
count=4932
count=4854
count=4732
count=4732

使用synchronized关键字

·```java

public class VolatileTest extends Thread {

volatile public static int count;
//一定要加static关键字,这样synchronized与static锁的内容就是VolatileTest类了,也就达到同步效果了。
synchronized 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) {
VolatileTest[] volatileTests = new VolatileTest[100];
for (int i = 0; i < 100; i++) {
volatileTests[i] = new VolatileTest();
}
for (int i = 0; i < 100; i++) {
volatileTests[i].start();
}
}

}

此时控制台打印结果如下:

......

count=9300

count=9400

count=9500

count=9600

count=9700

count=9800

count=9900

count=10000


### 使用原子类进行i++操作 ```java
package com.umgsai.thread.thread23;
public class AtomicIntegerTest extends Thread {
private AtomicInteger count = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(count.incrementAndGet());
}
} public static void main(String[] args) {
AtomicIntegerTest atomicIntegerTest = new AtomicIntegerTest();
Thread t1 = new Thread(atomicIntegerTest);
Thread t2 = new Thread(atomicIntegerTest);
Thread t3 = new Thread(atomicIntegerTest);
Thread t4 = new Thread(atomicIntegerTest);
Thread t5 = new Thread(atomicIntegerTest);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}

控制台打印结果如下:

......
4992
4993
4994
4995
4996
4997
4998
4999
5000

原子类也并不完全安全

package com.umgsai.thread.thread24
public class MyService {
public static AtomicLong atomicLong = new AtomicLong();
public void addNum() {
System.out.println(Thread.currentThread().getName() + " 加了100之后是:" + atomicLong.addAndGet(100));
atomicLong.addAndGet(1);
}
} public class MyThread extends Thread {
private MyService myService;
public MyThread(MyService myService) {
this.myService = myService;
} @Override
public void run() {
myService.addNum();
}
} public class Run {
public static void main(String[] args) {
try {
MyService myService = new MyService();
MyThread[] array = new MyThread[100];
for (int i = 0; i < array.length; i++) {
array[i] = new MyThread(myService);
}
for (int i = 0; i < array.length; i++) {
array[i].start();;
}
Thread.sleep(1000);
System.out.println(myService.atomicLong.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}

打印结果如下:

......
Thread-89 加了100之后是:8987
Thread-96 加了100之后是:9493
Thread-80 加了100之后是:9594
Thread-94 加了100之后是:9695
Thread-97 加了100之后是:9796
Thread-95 加了100之后是:9896
Thread-98 加了100之后是:9998
Thread-99 加了100之后是:10098
10100

累加的结果是正确的,但是打印顺序的错的,这是因为虽然addAndGet方法是原子的,但是方法和方法之间的调用却不是原子的。

解决方法:加上synchronized即可。

关键字synchronized可以使多个线程访问同一个资源具有同步性,而且还可以将线程工作内存中的私有变量与公共内存中的变量进行同步。

package com.umgsai.thread.thread25;
public class Service {
private boolean isContinueRun = true;
public void runMethod() {
while (isContinueRun) { }
System.out.println("stop...");
} public void stopMethod() {
isContinueRun = false;
}
}
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
this.service = service;
} @Override
public void run() {
service.runMethod();
}
} public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
} @Override
public void run() {
service.stopMethod();
}
}
public class Run {
public static void main(String[] args) {
try {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(1000);
ThreadB b = new ThreadB(service);
b.start();
System.out.println("已经发起停止命令了");
} catch (Exception e) {
e.printStackTrace();
}
}
}

出现死循环。

对Service做如下修改

public class Service {
private boolean isContinueRun = true;
public void runMethod() {
String anyString = new String();
while (isContinueRun) {
synchronized (anyString) { }
}
System.out.println("stop...");
} public void stopMethod() {
isContinueRun = false;
}
}

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

Java多线程编程核心技术---学习分享的更多相关文章

  1. java多线程编程核心技术学习-1

    实现多线程的两种方式 继承Thread类,重写Thread类中的run方法 public class MyThread extends Thread{ @Override public void ru ...

  2. 《java多线程编程核心技术》(一)使用多线程

    了解多线程 进程和多线程的概念和线程的优点: 提及多线程技术,不得不提及"进程"这个概念.百度百科对"进程"的解释如下: 进程(Process)是计算机中的程序 ...

  3. Java多线程编程核心技术(三)多线程通信

    线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...

  4. Java多线程编程核心技术(二)对象及变量的并发访问

    本文主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题.阅读本文应该着重掌握如下技术点: synchronized对象监视器为O ...

  5. Java多线程编程核心技术(一)Java多线程技能

    1.进程和线程 一个程序就是一个进程,而一个程序中的多个任务则被称为线程. 进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位. 举个例子: 打开你的计算机上的任务管 ...

  6. Java多线程编程核心技术---对象及变量的并发访问(二)

    数据类型String的常量池特性 在JVM中具有String常量池缓存的功能. public class Service { public static void print(String str){ ...

  7. Java多线程编程核心技术

    Java多线程编程核心技术 这本书有利于对Java多线程API的理解,但不容易从中总结规律. JDK文档 1. Thread类 部分源码: public class Thread implements ...

  8. 《Java多线程编程核心技术》推荐

    写这篇博客主要是给猿友们推荐一本书<Java多线程编程核心技术>. 之所以要推荐它,主要因为这本书写得十分通俗易懂,以实例贯穿整本书,使得原本抽象的概念,理解起来不再抽象. 只要你有一点点 ...

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

    作为业务开发人员,能够在工作中用到的技术其实不多.虽然平时老是说什么,多线程,并发,注入,攻击!但是在实际工作中,这些东西不见得用得上.因为,我们用的框架已经把这些事做掉了. 比如web开发,外面有大 ...

随机推荐

  1. 交叉编译总结 libosscore.a libcurl.a libmysqlclient.a

    把工程文件交叉编译到arm上运行,着实花费了一番功夫. 首先遇到的错误是 X uses VFP register arguments, B does not 百度了一下,发现是硬浮点和软浮点的问题,原 ...

  2. java操作hdfs实例

    环境:window7+eclipse+vmware虚拟机+搭建好的hadoop环境(master.slave01.slave02) 内容:主要是在windows环境下,利用eclipse如何来操作hd ...

  3. 基于GPUImage的多滤镜rtmp直播推流

    之前做过开源videocore的推流改进:1)加入了美颜滤镜; 2) 加入了librtmp替换原来过于简单的rtmpclient: 后来听朋友说,在videocore上面进行opengl修改,加入新的 ...

  4. 【小白的CFD之旅】16 流程

    那天听了小牛师兄关于CFD应用的四种境界的说法后,小白发现自己连第一种境界都算不上,自己对于CFD还只是停留在做了少数几个案例的基础上,可以说是对其一无所知.不过小白不是那种遇到挫折就退缩的人,他决定 ...

  5. 【2016-11-13】【坚持学习】【Day25】【Oracle 查询某一字段最大值】

    partition 分组order by 以哪一个字段排序 select n.* ,row_number()over(partition by n.type order by n.createtime ...

  6. codevs 1115 开心的金明--01背包

    1115 开心的金明 2006年NOIP全国联赛普及组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题目描述 Description 金明今天很开心,家里购 ...

  7. 水泡动画模拟(Marching Cubes)

    Marching Cubes算法是三维离散数据场中提取等值面的经典算法,其主要应用于医学领域的可视化场景,例如CT扫描和MRI扫描的3D重建等. 算法主要的思想是在三维离散数据场中通过线性插值来逼近等 ...

  8. spring的@Transactional

    在service类前加上@Transactional,声明这个service所有方法需要事务管理.每一个业务方法开始时都会打开一个事务.Spring默认情况下会对运行期例外(RunTimeExcept ...

  9. PL/SQL 如何查看当前连接信息以及SQL PLUS如何指定IP地址

    1.通过PL/SQL 想连接别的数据库可以通过在服务名前面加上IP和左斜线来实现: 2.有时我们的IP和左斜线不用输也能连别的数据库,是因为在一个文件里配置好了.这个文件在哪里? 在这个路径下的 NE ...

  10. PhpStorm XDebug 远程调试

    现在我们自己公司的各种开发和测试服务器,都是使用阿里云的服务器.一些PHP的项目,无法在本地搭建完整的环境,在外网服务器上调试更方便定位问题.发现网上没有完整的关于如何配置PHPStorm和XDebu ...