文章目录
概念
1、程序:由若干条具有一定功能的指令所组成的解题顺序和步骤
2、进程:程序的一次执行,是资源分配的最小单元
3、 线程:CPU的基本调度单位
抛开各种技术细节,从应用程序角度讲:
==
1、在单核计算机里,有一个资源是无法被多个程序并行使用的:cpu。
没有操作系统的情况下,一个程序一直独占着全都cpu。
如果要有两个任务来共享同一个CPU,程序员就需要仔细地为程序安排好运行计划–某时刻cpu和由程序A来独享,下一时刻cpu由程序B来独享
而这种安排计划后来成为OS的核心组件,被单独名命为“scheduler”,即“调度器”,它关心的只是怎样把单个cpu的运行拆分成一段一段的“运行片”,轮流分给不同的程序去使用,而在宏观上,因为分配切换的速度极快,就制造出多程序并行在一个cpu上的假象。
2、在单核计算机里,有一个资源可以被多个程序共用,然而会引出麻烦:内存。
在一个只有调度器,没有内存管理组件的操作系统上,程序员需要手工为每个程序安排运行的空间 – 程序A使用物理地址0x00-0xff,程序B使用物理地址0x100-0x1ff,等等。
然而这样做有个很大的问题:每个程序都要协调商量好怎样使用同一个内存上的不同空间,软件系统和硬件系统千差万别,使这种定制的方案没有可行性。
为了解决这个麻烦,计算机系统引入了“虚拟地址”的概念,从三方面入手来做:
2.1、硬件上,CPU增加了一个专门的模块叫MMU,负责转换虚拟地址和物理地址。
2.2、操作系统上,操作系统增加了另一个核心组件:memory management,即内存管理模块,它管理物理内存、虚拟内存相关的一系列事务。
2.3、应用程序上,发明了一个叫做【进程】的模型,(注意==)每个进程都用【完全一样的】虚拟地址空间,然而经由操作系统和硬件MMU协作,映射到不同的物理地址空间上==。不同的【进程】,都有各自独立的物理内存空间,不用一些特殊手段,是无法访问别的进程的物理内存的。
3、现在,不同的应用程序,可以不关心底层的物理内存分配,也不关心CPU的协调共享了。然而还有一个问题存在:有一些程序,想要共享CPU,【并且还要共享同样的物理内存】,这时候,一个叫【线程】的模型就出现了,它们被包裹在进程里面,在调度器的管理下共享CPu,拥有同样的虚拟地址空间,同时也共享同一个物理地址空间,然而,它们无法越过包裹自己的进程,去访问别一个进程的物理地址空间。
4、进程之间怎样共享同一个物理地址空间呢?不同的系统方法各异,符合posix规范的操作系统都提供了一个接口,叫mmap,可以把一个物理地址空间映射到不同的进程中,由不同的进程来共享。
5、PS:在有的操作系统里,进程不是调度单位(即不能被调度器使用),线程是最基本的调度单位,调度器只调度线程,不调度进程,比如VxWorks
一个线程的生命周期
- 新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
- 就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
- 运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
- 阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
- 死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
线程的优先级
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
线程的优先级只是理论上的优先级,它可以让线程优先级高的可能概率稍微大的先执行,但是具体执行哪一个线程却是由电脑控制的。众所周知,计算机对于线程的执行是抢占式策略,谁能抢到谁先执行,因此这个方案并不能完全控制顺序。
来自一些问答:
1、优先级高不代表说一定是优先级最高的运行完了再让优先级地的运行。否则那样多线程就没意义了。
优先级高就是说同等条件下我运行的概率会比优先级低的高,至于具体怎么分配,这样看虚拟机的策略。但是肯定不会是像你想的那样,优先级高的运行完才运行优先级低的
2、首先说 线程优先级,并不能保证优先级高的先运行,也不保证优先级高的更多的分配CPU时间,只是对系统的建议而已,到底运行哪个,是操作系统决定的,都不是java说了算的。
另外java只能保证在线程内部看起来是顺序执行你的代码的,并不能保证从其他线程看来这个是按照你编码顺序执行的。
3、根据java语言规范,程序的正确性和性能保障,不能依靠优先级。
要靠程序自己控制。
优先级这个东西一般在系统资源紧缺的情况下可能会比较明显。
你这个经过运行时优化之后,几乎没有什么了。
所以体现不出来。
创建一个线程
Java 提供了三种创建线程的方法:
- 通过实现 Runnable 接口;
- 通过继承 Thread 类本身;
- 通过 Callable 和 Future 创建线程。
通过实现 Runnable 接口来创建线程
class RunableDemo implements Runnable{
private Thread t;
private String name;
RunableDemo(String name){
this.name = name;
System.out.println("create:"+this.name);
}
public void run(){
int i;
try{
for(i=4;i>0;i--){
System.out.println("run Thread:"+this.name+","+i);
System.out.println("sleep:"+this.name+","+i);
Thread.sleep(50);
}
}catch(InterruptedException e){
System.out.println("interrupted:"+name);
}
System.out.println("exiting:"+this.name);
}
public void start(){
System.out.println("--------");
System.out.println("Thread start:"+this.name);
if(t==null){
t = new Thread(this,name);//创建名为name的thread
t.start();//不是上面的start();
}
}
}
public class ThreadTest{
public static void main(String[] args){
RunableDemo r1 = new RunableDemo("gyy1");
//r1.t = new Thread("gyy1");//错误,不能访问private
r1.start();
RunableDemo r2 = new RunableDemo("gyy2");
r2.start();
}
}
结果:有可能gyy1先结束,又可能gyy2先结束。
E:\java_code\code>java ThreadTest
create:gyy1
--------
Thread start:gyy1
create:gyy2
--------
run Thread:gyy1,4
Thread start:gyy2
sleep:gyy1,4
run Thread:gyy2,4
sleep:gyy2,4
run Thread:gyy1,3
run Thread:gyy2,3
sleep:gyy1,3
sleep:gyy2,3
run Thread:gyy1,2
sleep:gyy1,2
run Thread:gyy2,2
sleep:gyy2,2
run Thread:gyy2,1
run Thread:gyy1,1
sleep:gyy2,1
sleep:gyy1,1
exiting:gyy2
exiting:gyy1
通过继承Thread来创建线程
程序1
//线程创建3种方法
//继承线程
class ThreadTest1 extends Thread{
public void run(){
System.out.println("线程名:"+getName());
System.out.println("id名:"+getId());
}
}
public class Thread3Test{
public static void main(String[] args){
ThreadTest1 t1= new ThreadTest1();
t1.start();
//t1运行结束才能执行其他
//抛出异常,让jvm处理
try{
t1.join();
}catch(InterruptedException e){
e.printStackTrace();
}
ThreadTest1 t2= new ThreadTest1();
//设置线程名
t2.setName("gyy");
t2.start();
}
}
线程名:Thread-0
id名:10
线程名:gyy
id名:11
程序2
//线程创建3种方法
//继承线程
class ThreadTest1 extends Thread{
public void run(){
System.out.println("run运行:");
System.out.println("线程名:"+getName());
System.out.println("id名:"+getId());
System.out.println("priority:"+getPriority());
System.out.println("run中循环:");
for(int i=0;i<2;i++){
System.out.println("线程名:"+getName());
System.out.println("id名:"+getId());
}
}
}
public class Thread3Test{
public static void main(String[] args){
ThreadTest1 t1= new ThreadTest1();
System.out.println("————线程0————:");
System.out.println("main中getName()跟getId():");
System.out.println(t1.getName());
System.out.println(t1.getId());
System.out.println(t1.getPriority());
t1.setPriority(Thread.MIN_PRIORITY);
t1.start();
//t1运行结束才能执行其他
//抛出异常,让jvm处理
try{
t1.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("————线程1————:");
ThreadTest1 t2= new ThreadTest1();
//设置线程名
t2.setName("gyy");
t2.setPriority(Thread.MAX_PRIORITY);//1-10
t2.start();
}
}
————线程0————:
main中getName()跟getId():
Thread-0
10
5
run运行:
线程名:Thread-0
id名:10
priority:1
run中循环:
线程名:Thread-0
id名:10
线程名:Thread-0
id名:10
————线程1————:
run运行:
线程名:gyy
id名:11
priority:10
run中循环:
线程名:gyy
id名:11
线程名:gyy
id名:11
程序3
//class RunableDemo implements Runnable{
class RunableDemo extends Thread{
private Thread t;
private String name;
RunableDemo(String name){
this.name = name;
System.out.println("create:"+this.name);
}
public void run(){
int i;
try{
for(i=4;i>0;i--){
System.out.println("run Thread:"+this.name+","+i);
System.out.println("sleep:"+this.name+","+i);
Thread.sleep(50);
}
}catch(InterruptedException e){
System.out.println("interrupted:"+name);
}
System.out.println("exiting:"+this.name);
}
public void start(){
System.out.println("--------");
System.out.println("Thread start:"+this.name);
if(t==null){
t = new Thread(this,name);//创建名为name的thread
t.start();//不是上面的start();
}
}
}
public class ThreadTest{
public static void main(String[] args){
RunableDemo r1 = new RunableDemo("gyy1");
//r1.t = new Thread("gyy1");//错误,不能访问private
//因为类内才可以访问,他们不在同一个类
r1.start();
RunableDemo r2 = new RunableDemo("gyy2");
r2.start();
}
}
程序4
class ThreadTest1 extends Thread{
//重写
public void run(){
System.out.println("ThreadTest1 run运行:");
System.out.println("线程名:"+getName());
System.out.println("id名:"+getId());
System.out.println("priority:"+getPriority());
System.out.println("run中循环:");
for(int i=0;i<2;i++){
System.out.println("线程名:"+getName());
System.out.println("id名:"+getId());
}
}
}
class ThreadTest2 implements Runnable{
public void run(){
// 实现Runnable接口的,无法直接使用getId(),getName()等方法
// 需要使用Thread.currentThread() 来获取到当前对象才行
System.out.println("ThreadTest2 run运行:");
System.out.println("线程名2:"+Thread.currentThread().getName());
System.out.println("id名2:"+Thread.currentThread().getId());
System.out.println("priority2:"+Thread.currentThread().getPriority());
System.out.println("run中循环2:");
for(int i=0;i<2;i++){
System.out.println("线程名2:"+Thread.currentThread().getName());
System.out.println("id名2:"+Thread.currentThread().getId());
}
}
}
public class Thread3Test{
public static void main(String[] args){
ThreadTest1 t1= new ThreadTest1();
System.out.println("————线程0————:");
System.out.println("main中getName()跟getId():");
System.out.println(t1.getName());
System.out.println(t1.getId());
System.out.println(t1.getPriority());
t1.setPriority(Thread.MIN_PRIORITY);
t1.start();
//t1运行结束才能执行其他
//抛出异常,让jvm处理
try{
t1.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("————线程1————:");
ThreadTest1 t2= new ThreadTest1();
//设置线程名
t2.setName("gyy");
t2.setPriority(Thread.MAX_PRIORITY);//1-10
t2.start();
// t2.yield();
ThreadTest2 t3=new ThreadTest2();
Thread t31=new Thread(t3);
Thread t32=new Thread(t3,"gyy2");
System.out.println(t31.isAlive());
System.out.println(t32.isAlive());
t31.start();
t32.start();
System.out.println(t31.isAlive());
System.out.println(t32.isAlive());
————线程0————:
main中getName()跟getId():
Thread-0
10
5
ThreadTest1 run运行:
线程名:Thread-0
id名:10
priority:1
run中循环:
线程名:Thread-0
id名:10
线程名:Thread-0
id名:10
————线程1————:
ThreadTest1 run运行:
线程名:gyy
false
false
id名:11
ThreadTest2 run运行:
true
true
线程名2:Thread-2
ThreadTest2 run运行:
priority:10
run中循环:
线程名2:gyy2
id名2:12
id名2:13
线程名:gyy
id名:11
priority2:5
priority2:5
run中循环2:
run中循环2:
线程名:gyy
线程名2:gyy2
线程名2:Thread-2
id名2:13
id名:11
线程名2:gyy2
id名2:12
id名2:13
线程名2:Thread-2
id名2:12
程序
1、通过键盘输入一个数字,计算机循环随机得到一个数字看是否与输入的数字相等,如果不相等继续循环,反之停止。
import java.util.Scanner;
class GuessTest extends Thread{
private int number;
GuessTest(int number){
this.number = number;
System.out.println("number:"+this.number+",yes or not?");
}
public void run(){
int guess;
int count=0;
do{
guess =(int)(Math.random()*100+1);
System.out.println(this.getName()+" guess:"+guess);
count++;
}while(guess!=this.number);
System.out.println("okok!yes:"+this.getName()+":guess:"+guess+",count:"+count);;
}
}
public class GuessNumber{
public static void main(String[] args){
int i=0;
Scanner s = new Scanner(System.in);
System.out.println("请输入你的预测数字(整数):");
if(s.hasNextInt()){
i = s.nextInt();
System.out.println("输入整数:"+i);
}else{
System.out.println("输入的不是整数。");
}
GuessTest g = new GuessTest(i);
g.start();
}
}
2、多线程方法的使用:包括各个方法及线程按顺序执行
//需要Thread与start
//采用Runnable接口
public class DisplayMessage implements Runnable{
private String mge;
DisplayMessage(String mge){
this.mge = mge;
}
public void run(){
System.out.println(mge);
System.out.println(mge);
// while(true){
// System.out.println(mge);
// }
}
}
//采用Thread类
class GuessANumber extends Thread{
private int number;
GuessANumber(int number){
this.number = number;
System.out.println("number:"+this.number+",yes or not?");
}
public void run(){
int guess;
int count=0;
do{
guess =(int)(Math.random()*100+1);
System.out.println(this.getName()+" guess:"+guess);
count++;
}while(guess!=this.number);
System.out.println("okok!yes:"+this.getName()+":guess:"+guess+",count:"+count);;
}
}
public class ThreadClassDemo{
public static void main(String[] args){
// //Runnable
// Runnable hello = new DisplayMessage("Hello");
// Thread thread1 = new Thread(hello);
// thread1.setDaemon(true);
// thread1.setName("hello");
// System.out.println("Starting hello thread...");
// thread1.start();
DisplayMessage r1 = new DisplayMessage("gyy1");
Thread th1 = new Thread(r1);
th1.setDaemon(true);
System.out.println("start "+th1.getName()+" Thread……");
th1.setName("ggyy1");
System.out.println("start "+th1.getName()+" Thread……");
th1.start();//打印的是mge不是name
Runnable r2 = new DisplayMessage("gyy2");
Thread th2 = new Thread(r2);
th2.setPriority(Thread.MIN_PRIORITY);
th2.setDaemon(true);
System.out.println("start "+th2.getName()+" Thread……");
th2.start();
//Thread
GuessANumber g1 = new GuessANumber(9);
System.out.println("Starting "+g1.getName()+"...");
g1.start();
try{//去掉会使得可能g2先执行。
g1.join();
}catch(InterruptedException e){
System.out.println("Thread interrupted.");
}
GuessANumber g2 = new GuessANumber(17);
System.out.println("Starting "+g2.getName()+"...");
g2.start();
System.out.println("main() is ending...");
}
}
3、让线程按顺序执行
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadDemo3{
public static void main(String[] args){
method3();
}
private static void method3(){
final Thread t1 = new Thread(new Runnable(){
@Override
public void run(){
System.out.println("111");
}
});
final Thread t2 = new Thread(new Runnable(){
@Override
public void run(){
System.out.println("222");
}
});
final Thread t3 = new Thread(new Runnable(){
@Override
public void run(){
System.out.println("333");
}
});
//创建一个newSingleThreadExecutor的线程池
ExecutorService ss = Executors.newSingleThreadExecutor();
//队列,先进先出
ss.submit(t1);
ss.submit(t2);
ss.submit(t3);
ss.shutdown();
}
}
通过 Callable 和 Future 创建线程
创建Callable接口的实现类,并实现clall()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。
具体:
- 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
- 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
- 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
- 调用 FutureTask 对象的 get(),注意不是Thread的对象噢!! 方法来获得子线程执行结束后的返回值。
//采用Callable和Future创建线程
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class CallableDemo implements Callable<Integer>{//创建Callable接口的实现类
public static void main(String[] args){
int i;
CallableDemo cc = new CallableDemo();//创建Callable接口的实现类的对象
FutureTask<Integer> ff = new FutureTask<Integer>(cc);//用FutureTask来包装Callable对象
for(i=0;i<100;i++){
System.out.println("线程:"+Thread.currentThread().getName()+"i的值:"+i);
if(i==20){
Thread tt = new Thread(ff,"gyy");//Future对象创建线程
tt.start();
}
}
System.out.println("循环结束");
try{
System.out.println("线程返回值:"+ff.get());//返回call里的返回值
}catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}
}
//call返回值
@Override
public Integer call() throws Exception{
int j;
for(j=0;j<100;j++){
System.out.println("线程:"+Thread.currentThread().getName()+"j值:"+j);
}
return j;
}
}
出错:
E:\java_code\code>javac CallableDemo.java
CallableDemo.java:31: 错误: 找不到符号
}catch(ExecutionException e){
^
符号: 类 ExecutionException
位置: 类 CallableDemo
1 个错误
解决:
将 ExecutionException 改成 Exception
这种解决不太好,因为抛出错误,最好不好直接抛出Exception
解决2:
import.java.util.concurrent.ExecutionException
//成功解决
程序1
//线程创建3种方法
//继承线程
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ExecutionException;
class ThreadTest1 extends Thread{
//重写
public void run(){
System.out.println("ThreadTest1 run运行:");
System.out.println("线程名:"+getName());
System.out.println("id名:"+getId());
System.out.println("priority:"+getPriority());
System.out.println("run中循环:");
for(int i=0;i<2;i++){
System.out.println("线程名:"+getName());
System.out.println("id名:"+getId());
}
}
}
class ThreadTest2 implements Runnable{
public void run(){
// 实现Runnable接口的,无法直接使用getId(),getName()等方法
// 需要使用Thread.currentThread() 来获取到当前对象才行
System.out.println("ThreadTest2 run运行:");
System.out.println("线程名2:"+Thread.currentThread().getName());
System.out.println("id名2:"+Thread.currentThread().getId());
System.out.println("priority2:"+Thread.currentThread().getPriority());
System.out.println("run中循环2:");
for(int i=0;i<2;i++){
System.out.println("线程名2:"+Thread.currentThread().getName());
System.out.println("id名2:"+Thread.currentThread().getId());
}
}
}
class ThreadTest3 implements Callable{
private String name;
private int id;
public String call(){
System.out.println("ThreadTest3 run运行:");
this.name=Thread.currentThread().getName();
this.id=Thread.currentThread().getId();
return this.name;
//return Thread.currentThread().getName();
}
}
public class Thread3Test{
public static void main(String[] args){
ThreadTest1 t1= new ThreadTest1();
System.out.println("————线程0————:");
System.out.println("main中getName()跟getId():");
System.out.println(t1.getName());
System.out.println(t1.getId());
System.out.println(t1.getPriority());
t1.setPriority(Thread.MIN_PRIORITY);
t1.start();
//t1运行结束才能执行其他
//抛出异常,让jvm处理
try{
t1.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("————线程1————:");
ThreadTest1 t2= new ThreadTest1();
//设置线程名
t2.setName("gyy");
t2.setPriority(Thread.MAX_PRIORITY);//1-10
t2.start();
// t2.yield();
ThreadTest2 t3=new ThreadTest2();
Thread t31=new Thread(t3);
Thread t32=new Thread(t3,"gyy2");
System.out.println(t31.isAlive());
System.out.println(t32.isAlive());
t31.start();
t32.start();
System.out.println(t31.isAlive());
System.out.println(t32.isAlive());
ThreadTest3 t4= new ThreadTest3();
FutureTask ft4=new FutureTask(t4);
Thread t41=new Thread(ft4,"gyy4");
t41.start();
//String ff=(String)ft4.get();//出错
try{
String ff=(String)ft4.get();
//String //ff=ft4.get();出错,object不能转成string
System.out.println("---------"+ff);
}catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}
}
}
————线程0————:
main中getName()跟getId():
Thread-0
10
5
ThreadTest1 run运行:
线程名:Thread-0
id名:10
priority:1
run中循环:
线程名:Thread-0
id名:10
线程名:Thread-0
id名:10
————线程1————:
ThreadTest1 run运行:
线程名:gyy
false
false
id名:11
true
true
ThreadTest2 run运行:
ThreadTest2 run运行:
priority:10
run中循环:
ThreadTest3 run运行:
线程名2:Thread-2
线程名2:gyy2
id名2:12
---------gyy4
线程名:gyy
priority2:5
id名2:13
run中循环2:
id名:11
线程名2:Thread-2
priority2:5
run中循环2:
id名2:12
线程名:gyy
线程名2:Thread-2
线程名2:gyy2
id名2:12
id名:11
id名2:13
线程名2:gyy2
id名2:13
创建线程的三种方式的对比
- 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
- 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
线程的几个主要概念
在多线程编程时,你需要了解以下几个概念:
线程同步
线程间通信
线程死锁
线程控制:挂起、停止和恢复
多线程的使用
有效利用多线程的关键是理解程序是并发执行而不是串行执行的。例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程。
通过对多线程的使用,可以编写出非常高效的程序。不过请注意,如果你创建太多的线程,程序执行的效率实际上是降低了,而不是提升了。
请记住,上下文的切换开销也很重要,如果你创建了太多的线程,CPU 花费在上下文的切换的时间将多于执行程序的时间!
线程同步
- 同步代码块(锁对象,run里的代码)
- 同步方法(成员方法)
程序1(未同步)
通过模拟取钱过程演示线程安全问题。
一般取钱过程:
1. 用户输入账户、密码,系统判断用户的账户、密码是否匹配。
2. 用户输入取款金额。
3. 系统判断账户余额是否大于取款金额。
4. 如果余额大于取款金额,则取款成功;如果余额小于取款金额,则取款失败。
在模拟过程中就模拟后面三步,没有模拟账户密码验证过程。
出错是因为信息不能及时同步
不是因为设置bb变量
//创建一个线程模拟存钱
//用户
import java.io.Serializable;
import java.util.Random;
class Account implements Serializable{
private String number;
private double count;
//设置账户
Account(String number,double count){
this.number=number;
this.count=count;
}
public double getMoney(){
return this.count;
}
public String getNumber(){
return this.number;
}
public void setMoney(double count){
this.count=count;
}
}
//取钱
class TakeMoney1 extends Thread{
private String name;
private Account acount;
private double take;
TakeMoney1(String name,Account acount,double take){
this.name=name;
this.acount=acount;
this.take=take;
}
public void run(){
if(this.acount.getMoney()>=this.take){
System.out.println(this.name+" 取钱成功,取出:"+this.take);
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
//变量要传到setMoney之后钱数才会变
//这期间可能有其他人取了钱
//如果在结算的同时,直接把余额传到setMoney,这期间不会受影响
//但是会出现透支
double dd=this.acount.getMoney()-this.take;
System.out.println(this.name+" 取钱后,剩余:"+dd);
this.acount.setMoney(dd);
// System.out.println(this.name+" 取钱后,剩余:"+(this.acount.getMoney()-this.take));
//再访问了一次,所以不会出错
// this.acount.setMoney(this.acount.getMoney()-this.take);//不会出错
}else{
System.out.println(this.name+" 取钱失败");
//System.out.println(this.name+" 账户余额:"+this.acount.getMoney());//不会出错
}
}
}
//A、B取钱
public class Thread1MoneyTest{
public static void main(String[] args){
Account a=new Account("111",5000);
TakeMoney1 t1=new TakeMoney1("A",a,900);
TakeMoney1 t2=new TakeMoney1("B",a,4500);
Random rr=new Random();
int ri=rr.nextInt(100);
if(ri%2==0){
t1.start();
t2.start();
}else{
t1.start();
t2.start();
}
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(a.getNumber()+" "+a.getMoney());
}
}
E:\java_code\code\jvmclass>java Thread1MoneyTest
B 取钱成功,取出:4500.0
A 取钱成功,取出:900.0
B 取钱后,剩余:500.0
A 取钱后,剩余:-400.0
111 -400.0
E:\java_code\code\jvmclass>javac Thread1MoneyTest.java
E:\java_code\code\jvmclass>java Thread1MoneyTest
A 取钱成功,取出:900.0
B 取钱成功,取出:4500.0
B 取钱后,剩余:500.0
A 取钱后,剩余:4100.0
111 4100.0
透支:
//创建一个线程模拟存钱
//同步代码块演示
//用户
import java.io.Serializable;
import java.util.Random;
class Account implements Serializable{
private String number;
private double count;
//设置账户
Account(String number,double count){
this.number=number;
this.count=count;
}
public double getMoney(){
return this.count;
}
public String getNumber(){
return this.number;
}
public void setMoney(double count){
this.count=count;
}
}
//取钱
class TakeMoney1 extends Thread{
private String name;
private Account acount;
private double take;
TakeMoney1(String name,Account acount,double take){
this.name=name;
this.acount=acount;
this.take=take;
}
public void run(){
//synchronized(acount){
if(this.acount.getMoney()>=this.take){
System.out.println(this.name+" 取钱成功,取出:"+this.take);
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
double dd=this.acount.getMoney()-this.take;
//System.out.println(this.name+" 取钱后,剩余:"+dd);
//this.acount.setMoney(dd);
System.out.println(this.name+" 取钱后,剩余:"+(this.acount.getMoney()-this.take));
this.acount.setMoney(this.acount.getMoney()-this.take);
}else{
System.out.println(this.name+" 取钱失败");
//System.out.println(this.name+" 账户余额:"+this.acount.getMoney());//不会出错
}
}
//}
}
//A、B取钱
public class Thread1MoneyTest{
public static void main(String[] args){
Account a=new Account("111",5000);
TakeMoney1 t1=new TakeMoney1("A",a,900);
TakeMoney1 t2=new TakeMoney1("B",a,4500);
Random rr=new Random();
int ri=rr.nextInt(100);
if(ri%2==0){
t1.start();
t2.start();
}else{
t1.start();
t2.start();
}
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(a.getNumber()+" "+a.getMoney());
}
}
B 取钱成功,取出:4500.0
A 取钱成功,取出:900.0
B 取钱后,剩余:500.0
A 取钱后,剩余:-400.0
111 -400.0
同步代码块(同步监视器)
synchronized (obj) {
//同步代码块
//任何线程进入下面同步代码块之前必须先获得对obj的锁;
//锁对象可以是任意对象,但必须保证是同一对象
}
//创建一个线程模拟存钱
//用户
import java.io.Serializable;
import java.util.Random;
class Account implements Serializable{
private String number;
private double count;
//设置账户
Account(String number,double count){
this.number=number;
this.count=count;
}
public double getMoney(){
return this.count;
}
public String getNumber(){
return this.number;
}
public void setMoney(double count){
this.count=count;
}
}
//取钱
class TakeMoney1 extends Thread{
private String name;
private Account acount;
private double take;
TakeMoney1(String name,Account acount,double take){
this.name=name;
this.acount=acount;
this.take=take;
}
public void run(){
//加锁
synchronized(acount){
if(this.acount.getMoney()>=this.take){
System.out.println(this.name+" 取钱成功,取出:"+this.take);
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
double dd=this.acount.getMoney()-this.take;
System.out.println(this.name+" 取钱后,剩余:"+dd);
this.acount.setMoney(dd);
// System.out.println(this.name+" 取钱后,剩余:"+(this.acount.getMoney()-this.take));
// this.acount.setMoney(this.acount.getMoney()-this.take);//不会出错
}else{
System.out.println(this.name+" 取钱失败");
//System.out.println(this.name+" 账户余额:"+this.acount.getMoney());//不会出错
}
}
}//释放同步锁
}
//A、B取钱
public class Thread1MoneyTest{
public static void main(String[] args){
Account a=new Account("111",5000);
TakeMoney1 t1=new TakeMoney1("A",a,900);
TakeMoney1 t2=new TakeMoney1("B",a,4500);
Random rr=new Random();
int ri=rr.nextInt(100);
if(ri%2==0){
t1.start();
t2.start();
}else{
t1.start();
t2.start();
}
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(a.getNumber()+" "+a.getMoney());
}
}
E:\java_code\code\jvmclass>java Thread1MoneyTest
A 取钱成功,取出:900.0
A 取钱后,剩余:4100.0
B 取钱失败
111 4100.0
同步方法
如果把执行放在类中,会错更多
- 以下代码也会出错:
//创建一个线程模拟存钱
//同步方法
//用户
import java.io.Serializable;
import java.util.Random;
class Account implements Serializable{
private String number;
private double count;
//设置账户
Account(String number,double count){
this.number=number;
this.count=count;
}
public double getMoney(){
return this.count;
}
public String getNumber(){
return this.number;
}
public void setMoney(double count){
this.count=count;
}
//同步方法
public void takeMoney(String name,double take){
if(take<=this.count){
System.out.println(name+" 取钱成功,取出:"+take);
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
double dd=this.count-take;
System.out.println(name+" 取钱后,剩余:"+dd);
this.setMoney(dd);
}else{
System.out.println(name+" 取钱失败");
System.out.println("原因"+this.number+" 余额不足,剩余:"+this.count);
}
}
}
//取钱
class TakeMoney11 extends Thread{
private String name;
private Account acount;
private double take;
TakeMoney11(String name,Account acount,double take){
this.name=name;
this.acount=acount;
this.take=take;
}
public void run(){
acount.takeMoney(this.name,this.take);
}
}
//A、B取钱
public class Thread2MoneyTest{
public static void main(String[] args){
Account a=new Account("111",5000);
TakeMoney11 t1=new TakeMoney11("A",a,900);
TakeMoney11 t2=new TakeMoney11("B",a,4500);
Random rr=new Random();
int ri=rr.nextInt(100);
if(ri%2==0){
t1.start();
t2.start();
}else{
t1.start();
t2.start();
}
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(a.getNumber()+" "+a.getMoney());
}
}
E:\java_code\code\jvmclass>java Thread2MoneyTest
B 取钱成功,取出:4500.0
A 取钱成功,取出:900.0
B 取钱后,剩余:500.0
A 取钱后,剩余:4100.0
111 4100.0
- 透支
//创建一个线程模拟存钱
//用户
import java.io.Serializable;
import java.util.Random;
class Account implements Serializable{
private String number;
private double count;
//设置账户
Account(String number,double count){
this.number=number;
this.count=count;
}
public double getMoney(){
return this.count;
}
public String getNumber(){
return this.number;
}
public void setMoney(double count){
this.count=count;
}
public void takeMoney(String name,double take){
if(take<=this.count){
System.out.println(name+" 取钱成功,取出:"+take);
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
this.count=this.count-take;
System.out.println(name+" 取钱后,剩余:"+this.count);
}else{
System.out.println(name+" 取钱失败");
System.out.println("原因"+this.number+" 余额不足,剩余:"+this.count);
}
}
}
//取钱
class TakeMoney11 extends Thread{
private String name;
private Account acount;
private double take;
TakeMoney11(String name,Account acount,double take){
this.name=name;
this.acount=acount;
this.take=take;
}
public void run(){
acount.takeMoney(this.name,this.take);
}
}
//A、B取钱
public class Thread2MoneyTest{
public static void main(String[] args){
Account a=new Account("111",5000);
TakeMoney11 t1=new TakeMoney11("A",a,900);
TakeMoney11 t2=new TakeMoney11("B",a,4500);
Random rr=new Random();
int ri=rr.nextInt(100);
if(ri%2==0){
t1.start();
t2.start();
}else{
t1.start();
t2.start();
}
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(a.getNumber()+" "+a.getMoney());
}
}
E:\java_code\code\jvmclass>java Thread2MoneyTest
B 取钱成功,取出:4500.0
A 取钱成功,取出:900.0
B 取钱后,剩余:500.0
A 取钱后,剩余:-400.0
111 -400.0
- 采用同步方法
//创建一个线程模拟存钱
//同步方法
//用户
import java.io.Serializable;
import java.util.Random;
class Account implements Serializable{
private String number;
private double count;
//设置账户
Account(String number,double count){
this.number=number;
this.count=count;
}
public double getMoney(){
return this.count;
}
public String getNumber(){
return this.number;
}
public void setMoney(double count){
this.count=count;
}
//同步方法
public synchronized void takeMoney(String name,double take){
if(take<=this.count){
System.out.println(name+" 取钱成功,取出:"+take);
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
//采用变量才会出错,如果打印时一并把钱传入,就不会出错
this.count=this.count-take;
System.out.println(name+" 取钱后,剩余:"+this.count);
}else{
System.out.println(name+" 取钱失败");
System.out.println("原因"+this.number+" 余额不足,剩余:"+this.count);
}
}
}
//取钱
class TakeMoney11 extends Thread{
private String name;
private Account acount;
private double take;
TakeMoney11(String name,Account acount,double take){
this.name=name;
this.acount=acount;
this.take=take;
}
public void run(){
acount.takeMoney(this.name,this.take);
}
}
//A、B取钱
public class Thread2MoneyTest{
public static void main(String[] args){
Account a=new Account("111",5000);
TakeMoney11 t1=new TakeMoney11("A",a,900);
TakeMoney11 t2=new TakeMoney11("B",a,4500);
Random rr=new Random();
int ri=rr.nextInt(100);
if(ri%2==0){
t1.start();
t2.start();
}else{
t1.start();
t2.start();
}
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(a.getNumber()+" "+a.getMoney());
}
}
E:\java_code\code\jvmclass>java Thread2MoneyTest
A 取钱成功,取出:900.0
A 取钱后,剩余:4100.0
B 取钱失败
原因111 余额不足,剩余:4100.0
111 4100.0
同步锁
//创建一个线程模拟存钱
//同步锁
//用户
import java.io.Serializable;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
class Account implements Serializable{
//创建锁对象
private final ReentrantLock lock =new ReentrantLock();
private String number;
private double count;
//设置账户
Account(String number,double count){
this.number=number;
this.count=count;
}
public double getMoney(){
return this.count;
}
public String getNumber(){
return this.number;
}
public void setMoney(double count){
this.count=count;
}
public void takeMoney(String name,double take){
//加锁
lock.lock();
try{
if(take<=this.count){
System.out.println(name+" 取钱成功,取出:"+take);
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
this.count=this.count-take;
System.out.println(name+" 取钱后,剩余:"+this.count);
}else{
System.out.println(name+" 取钱失败");
System.out.println("原因"+this.number+" 余额不足,剩余:"+this.count);
}
}finally{
lock.unlock();
}
}
}
//取钱
class TakeMoney11 extends Thread{
private String name;
private Account acount;
private double take;
TakeMoney11(String name,Account acount,double take){
this.name=name;
this.acount=acount;
this.take=take;
}
public void run(){
acount.takeMoney(this.name,this.take);
}
}
//A、B取钱
public class Thread3MoneyTest{
public static void main(String[] args){
Account a=new Account("111",5000);
TakeMoney11 t1=new TakeMoney11("A",a,900);
TakeMoney11 t2=new TakeMoney11("B",a,4500);
Random rr=new Random();
int ri=rr.nextInt(100);
if(ri%2==0){
t1.start();
t2.start();
}else{
t1.start();
t2.start();
}
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(a.getNumber()+" "+a.getMoney());
}
}
E:\java_code\code\jvmclass>java Thread3MoneyTest
A 取钱成功,取出:900.0
A 取钱后,剩余:4100.0
B 取钱失败
原因111 余额不足,剩余:4100.0
111 4100.0
死锁
/**
* 死锁演示示例
*
* @author sxj
*
*/
public class DeadLockDemo {
public static void main(String[] args) {
final A a = new A();
final B b = new B();
new Thread(new Runnable() {
@Override
public void run() {
a.one(b);
}
}, "线程A").start();
new Thread(new Runnable() {
@Override
public void run() {
b.one(a);
}
}, "线程B").start();
}
}
class A {
/**
* 同步方法
*
* @param b
*/
public synchronized void one(B b) {
System.out.println(Thread.currentThread().getName()
+ " 进入了A实例的one()方法");
// 等待200ms
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " 企图调用B实例的two()方法");
b.two();
}
public synchronized void two() {
System.out.println("进入了A类的two()方法");
}
}
class B {
/**
* 同步方法
*
* @param a
*/
public synchronized void one(A a) {
System.out.println(Thread.currentThread().getName()
+ " 进入了B实例的one()方法");
// 等待200ms
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " 企图调用A实例的two()方法");
a.two();
}
public synchronized void two() {
System.out.println("进入了B类的two()方法");
}
}
E:\java_code\code\jvmclass>java DeadLockDemo
线程A 进入了A实例的one()方法
线程B 进入了B实例的one()方法
线程A 企图调用B实例的two()方法
线程B 企图调用A实例的two()方法
(没有结束)
//A调用B,要等待B的one结束
//B要调用A,要等待A的one结束,因为one有同步方法
- 去掉A的同步方法
/**
* 死锁演示示例
*
* @author sxj
*
*/
public class DeadLockDemo {
public static void main(String[] args) {
final A a = new A();
final B b = new B();
new Thread(new Runnable() {
@Override
public void run() {
a.one(b);
}
}, "线程A").start();
new Thread(new Runnable() {
@Override
public void run() {
b.one(a);
}
}, "线程B").start();
}
}
class A {
/**
* 同步方法
*
* @param b
*/
public void one(B b) {
System.out.println(Thread.currentThread().getName()
+ " 进入了A实例的one()方法");
// 等待200ms
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " 企图调用B实例的two()方法");
b.two();
}
public synchronized void two() {
System.out.println("进入了A类的two()方法");
}
}
class B {
/**
* 同步方法
*
* @param a
*/
public synchronized void one(A a) {
System.out.println(Thread.currentThread().getName()
+ " 进入了B实例的one()方法");
// 等待200ms
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " 企图调用A实例的two()方法");
a.two();
}
public synchronized void two() {
System.out.println("进入了B类的two()方法");
}
}
E:\java_code\code\jvmclass>java DeadLockDemo
线程A 进入了A实例的one()方法
线程B 进入了B实例的one()方法
线程A 企图调用B实例的two()方法
线程B 企图调用A实例的two()方法
进入了A类的two()方法
进入了B类的two()方法
//可以了
阻塞
wait、notify/notifyAll
- wait notify
- 交替循环
class Printer{
private int flag=1;
public void printer1(){
synchronized (this){
if(flag!=1){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print(1);
System.out.print(1);
System.out.print(1);
System.out.print(1);
System.out.print(1);
System.out.print(1);
System.out.println();
flag=2;
this.notify();
}
}
public void printer2(){
synchronized (this){
if(flag!=2){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print(2);
System.out.print(2);
System.out.print(2);
System.out.print(2);
System.out.print(2);
System.out.print(2);
flag=1;
this.notify();
}
}
}
public class NotifyTest{
public static void main(String[] args){
final Printer pp=new Printer();
new Thread(){
public void run(){
while(true){
pp.printer1();
}
}
}.start();
new Thread(){
public void run(){
while(true){
pp.printer2();
}
}
}.start();
}
}
22221111
22221111
22221111
22221111
22221111
22221111
22221111
22221111
22221111
//一直交替循环
- 可能阻塞,一直在等
- 采用while
class Printer{
private int flag=1;
public void printer1(){
synchronized (this){
while(flag!=1){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print("1");
System.out.print("1");
System.out.print("1");
System.out.print("1");
System.out.println();
flag=2;
this.notify();
//唤醒所有
}
}
public void printer2(){
synchronized (this){
while(flag!=2){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print("2");
System.out.print("2");
System.out.print("2");
System.out.print("2");
flag=3;
this.notify();
}
}
public void printer3(){
synchronized (this){
while(flag!=3){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print("3");
System.out.print("3");
System.out.print("3");
System.out.print("3");
System.out.println();
flag=1;
this.notify();
}
}
}
public class NotifyAllTest{
public static void main(String[] args){
final Printer pp=new Printer();
new Thread(){
public void run(){
//一定要有while
while(true){
pp.printer1();
}
}
}.start();
new Thread(){
public void run(){
while(true){
pp.printer2();
}
}
}.start();
new Thread(){
public void run(){
while(true){
pp.printer3();
}
}
}.start();
}
}
E:\java_code\code\jvmclass>java NotifyAllTest
1111
22223333
- 循环不交替
//flag=1时,1执行,2,3阻塞
//flag=2时,2执行,1,3阻塞
//flag=3时,3执行,1,2阻塞
//执行具备:flag+唤醒或者if结束
///123的循环不一定交替
class Printer{
private int flag=1;
public void printer1(){
synchronized (this){
if(flag!=1){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print("1");
System.out.print("1");
System.out.print("1");
System.out.print("1");
System.out.println();
flag=2;
this.notify();
//唤醒所有
}
}
public void printer2(){
synchronized (this){
if(flag!=2){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print("2");
System.out.print("2");
System.out.print("2");
System.out.print("2");
flag=3;
this.notify();
}
}
public void printer3(){
synchronized (this){
if(flag!=3){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print("3");
System.out.print("3");
System.out.print("3");
System.out.print("3");
System.out.println();
flag=1;
this.notify();
}
}
}
public class NotifyAllTest{
public static void main(String[] args){
final Printer pp=new Printer();
new Thread(){
public void run(){
//一定要有while
while(true){
pp.printer1();
}
}
}.start();
new Thread(){
public void run(){
while(true){
pp.printer2();
}
}
}.start();
new Thread(){
public void run(){
while(true){
pp.printer3();
}
}
}.start();
}
}
22223333
22223333
1111
3333
1111
3333
1111
//循环不交替
wait,notyfyAll
//flag=1时,1执行,2,3阻塞
//flag=2时,2执行,1,3阻塞
//flag=3时,3执行,1,2阻塞
//执行具备:flag+唤醒
///123的循环交替
class Printer{
private int flag=1;
public void printer1(){
synchronized (this){
while(flag!=1){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print("1");
System.out.print("1");
System.out.print("1");
System.out.print("1");
System.out.println();
flag=2;
this.notifyAll();
//唤醒所有
}
}
public void printer2(){
synchronized (this){
while(flag!=2){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print("2");
System.out.print("2");
System.out.print("2");
System.out.print("2");
flag=3;
this.notifyAll();
}
}
public void printer3(){
synchronized (this){
while(flag!=3){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.print("3");
System.out.print("3");
System.out.print("3");
System.out.print("3");
System.out.println();
flag=1;
this.notifyAll();
}
}
}
public class NotifyAllTest{
public static void main(String[] args){
final Printer pp=new Printer();
new Thread(){
public void run(){
//一定要有while
while(true){
pp.printer1();
}
}
}.start();
new Thread(){
public void run(){
while(true){
pp.printer2();
}
}
}.start();
new Thread(){
public void run(){
while(true){
pp.printer3();
}
}
}.start();
}
}
1111
22223333
1111
22223333
1111
22223333
1111
22223333
1111
22223
//循环交替