Thread清晰解读
概念性理解
1.什么是进程?
程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。是系统进行资源分配和调度的基本单位。
2.什么是线程?
是进程中的一个实体,属于轻量级的进程;
3.有了进程后为什么还要有线程?
进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
4.进程和线程的区别
- 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
- 线程是进程的一个实体, 是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源
- 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。、
** 5.并发和并行**
并发和并行都可以标识两个或者多个任务一起执行,但是还是存在区别,并发偏重于多个任务交替进行,而并行就是同时执行
线程创建方式
线程创建的方式有两种,一种是实现Runnable,一种是继承Thread
/**
* @author 雀跃set
* @date 2019.09.19
*
*/
public class ThreadLook {
public static class thread1 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("thread1");
}
}
public static class thread2 extends Thread{
@Override
public void run() {
System.out.println("thread2");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t1=new Thread(new thread1());
Thread t2=new Thread(new thread2());
t1.start();
t2.start();
}
}
运行结果
注意这里启动线程使用的是start方法,而不是run,run方法只是线程要执行的方法体
这里可以看一下start的源码
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
明显可以看出来启动线程不只是执行run方法。下面就来把源码的方法好好的看一遍吧
通过Thread的构造器得到线程
Thread的构造器有以下几种
- Thread()
- Thread(Runnable target) 。target就是我们通过runnable或者Thread创建的线程对象
- Thread(ThreadGroup group, Runnable target) 。group指的是线程组,把线程分组,比如main方法体重执行的线程,其线程组就是main
- Thread(String name) 。name是线程名,如果未给,线程名就会通过"Thread-" + nextThreadNum()生成
- Thread(ThreadGroup group, String name)
- Thread(Runnable target, String name)
- Thread(ThreadGroup group, Runnable target, String name)
- Thread(ThreadGroup group, Runnable target, String name, long stackSize) 。stackSize 堆栈大小是虚拟机为该线程的堆栈分配的大致的地址空间字节数。 stackSize参数的影响(如果有的话)与平台有关。在某些平台上,指定了一个较高的值stackSize参数可以允许抛出一个前一个线程来实现更大的递归深度StackOverflowError 。 类似地,指定较低的值可能允许更多数量的线程同时存在,而不会抛出OutOfMemoryError (或其他内部错误)。 所述stackSize参数的值和最大递归深度和并发水平之间的关系的细节是依赖于平台的。 在某些平台上,该值stackSize参数可能没有任何效果。
下面我们就通过这几种方式线程得到线程的可执行对象,最后一种暂不考虑
/**
* @author 雀跃set
* @date 2019.09.19
*
*/
public class ThreadLook {
public static class thread1 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
System.out.println("thread1");
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t1=new Thread();
//Thread(Runnable target)
Thread t2=new Thread(new thread1());
//Thread(ThreadGroup group, Runnable target) 。
Thread t3=new Thread(new ThreadGroup("线程组测试"),new thread1());
//Thread(String name)
Thread t4=new Thread("线程名称");
//Thread(ThreadGroup group, String name)
Thread t5=new Thread(new ThreadGroup("线程组测试"),"线程名称");
//Thread(Runnable target, String name)
Thread t6=new Thread(new thread1(),"线程名称");
//Thread(ThreadGroup group, Runnable target, String name)
Thread t7=new Thread(new ThreadGroup("线程组测试"),new thread1(),"线程名称");
}
}
可以下这几个构造器的源代码
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
可以发现共同点
- 都是使用的init方法,只不过是传入的参数不同
- 没有线程名就会调用"Thread-" + nextThreadNum()生成线程名称
下面看一下这个方法的实现
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if null
* @param inheritThreadLocals if {@code true}, inherit initial values for
* inheritable thread-locals from the constructing thread
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;//线程名
Thread parent = currentThread();//当前线程,指的是调用此线程的线程,也就是父线程
SecurityManager security = System.getSecurityManager();//安全管理器,不看
if (g == null) {//线程组
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
//如果安全管理器存在,就通过安全管理器获取线程组,获取线程组是为了将线程添加到对应的线程组之中
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
//如果安全管理器为空或者通过安全管理器获取不到线程组,就直接获取父线程的线程组
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
//检查是否具备操作线程组的权限
g.checkAccess();
/*
* Do we have the required permissions?
*/
//还是鉴权
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
//将线程添加到线程组里
g.addUnstarted();
//变量赋值 线程组,是否守护线程以及优先级
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
//上下文加载器
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
//设置优先级
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
//设置线程id,每个线程只有一个,唯一标识
tid = nextThreadID();
}
代码的具体实现看下上面的注释就行了,这部分没有必要深究的。通过以上构造器,我们就获取到了一个可以启动的线程对象了,如果没有其他要求的话,我们就可以直接调用start方法启动线程了,此时我们可以获取一下这个线程的默认设置
查看线程的设置
- 优先级
- 线程名
- 线程ID
- 线程组
- 是否守护线程
- 调用线程
- 线程状态
下面就通过代码来获取一下
/**
* @author 雀跃set
* @date 2019.09.19
*
*/
public class ThreadLook {
public static class thread1 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
System.out.println("thread1");
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t1=new Thread();
//Thread(Runnable target)
Thread t2=new Thread(new thread1());
//Thread(ThreadGroup group, Runnable target) 。
Thread t3=new Thread(new ThreadGroup("线程组测试"),new thread1());
//Thread(String name)
Thread t4=new Thread("线程名称");
//Thread(ThreadGroup group, String name)
Thread t5=new Thread(new ThreadGroup("线程组测试"),"线程名称");
//Thread(Runnable target, String name)
Thread t6=new Thread(new thread1(),"线程名称");
//Thread(ThreadGroup group, Runnable target, String name)
Thread t7=new Thread(new ThreadGroup("线程组测试"),new thread1(),"线程名称");
System.out.println("线程t1:\n 优先级="+t1.getPriority()+";\n 线程名="+t1.getName()+";\n "
+ "线程ID="+t1.getId()+";\n 是否守护线程="+t1.isDaemon()+";\n 调用线程="+t1.currentThread()+";\n"
+ " 线程状态="+t1.getState()+";\n 线程组="+t1.getThreadGroup());
System.out.println("线程t7:\n 优先级="+t7.getPriority()+";\n 线程名="+t7.getName()+";\n "
+ "线程ID="+t7.getId()+";\n 是否守护线程="+t7.isDaemon()+";\n 调用线程="+t7.currentThread()+";\n"
+ " 线程状态="+t7.getState()+";\n 线程组="+t7.getThreadGroup());
}
}
执行结果
线程t1:
优先级=5;
线程名=Thread-0;
线程ID=11;
是否守护线程=false;
调用线程=Thread[main,5,main];
线程状态=NEW;
线程组=java.lang.ThreadGroup[name=main,maxpri=10]
线程t7:
优先级=5;
线程名=线程名称;
线程ID=17;
是否守护线程=false;
调用线程=Thread[main,5,main];
线程状态=NEW;
线程组=java.lang.ThreadGroup[name=线程组测试,maxpri=10]
我打印了两种构造器的,一种是什么参数都没传递的,一种是传入了线程组,线程以及线程名称;
下面来解释一下这几个参数的概念
1.优先级(默认为5)
多个线程并发执行的话,优先级高的会被先执行,当然这不是一定的。只能说会被优先调度,而不是一定被优先调度。
例子
public class demo01 {
public static class thread1 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("aaaa");
}
}
public static class thread2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("bbb");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t1=new Thread(new thread1());
Thread t2=new Thread(new thread2());
t1.setPriority(10);
t2.setPriority(5);
t1.start();
t2.start();
}
}
线程t1的优先级设置为10, 线程t2的优先级为5,但是执行了数次之后,会发现并不一定完全是 aaa先打印出来
将run中加上
//t1
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
System.out.println("aaa");
}
}
//t2
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
System.out.println("bbb");
}
}
执行结果也可以看到出现了bbb的打印结果。由此说明优先级高的会被优先调度,并不是必然事件。
2.线程名
在使用传递线程名参数的构造器中,线程名为传递进入的值,如果是没有传入参数的构造器中,线程名是"Thread-" + nextThreadNum()
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
有static synchronized两个关键字修饰之后,每次指挥有一个线程进入nextThreadNum方法,因此线程名称采用默认的话是不重复的。
3.线程ID
线程的唯一标识 ,有人就会好奇为啥不是从0开始的,这是因为在我们执行创建我们所需要的线程的时候就已经创建了一些必须的线程,比如我们的main等
线程ID是在线程创建的时候就被赋值的,在Init方法的最后一行
/* Set thread ID */
tid = nextThreadID();
//nextThreadID方法
/* For generating thread ID */
private static long threadSeqNumber;
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
同样被 static synchronized两个关键字修饰,唯一,线程标志
4.是否守护线程
守护线程是一种特殊线程,就如名字一样,守护系统运行的线程,最普遍的一个就是垃圾回收线程。与之对应的我们可以理解为用户线程,当一个程序中用户线程执行完毕,全部退出之后,就剩下了守护线程,守护线程要守护的对象不在了也就意味着程序要结束了,因此当一个java程序中只剩下守护线程,java虚拟机就会自然退出
例子:
public class demo01 {
public static class thread1 extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
System.out.println("aaaa");
}
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Thread t1=new thread1();
t1.setDaemon(true);
t1.start();
}
}
执行结果为没打印任何值,如果不是守护线程的话,就会一直打印aaaa,而因为把它设置成了守护线程,它就会等main用户线程执行结束。
5.调用线程
创建和执行子线程的线程
返回的值 为线程组名,优先级,线程名,方法是native方法,native方法是c语言写的,然后直接调用的。
6.线程状态
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
通过查看源代码,可以看到线程有NEW,RUNNABLE,WAITING,TIMED_WAITING,BLOCKED,TERMINATED;从网上看到这个图,觉得比较清晰点,具体的状态流转参考图就行了。
- NEW新建
- RUNNABLE 就绪状态等待运行
- running就属于抽象的了也是RUNNABLE中的
- WAITING 等待状态
- TIMED_WAITING 限时等待状态,和WAITING不同在于不释放锁,在等待时间结束之后就会继续执行
- BLOCKED 锁定状态
- TERMINATED 结束状态
7.线程组
线程组,获取线程所属的线程组,设置是在初始化的时候进行设置的。
了解线程状态图中的方法
1.start方法
线程的调度方法,看一下源代码
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
//判断当前线程的状态是否为新建状态,只有新建状态的才能被start方法启动
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
//把线程加入线程组中
group.add(this);
//先设置个标志,如果通过start0()方法启动成功就变为true
boolean started = false;
try {
start0();
started = true;
} finally {
try {
//如果启动失败,会被从线程组中删除掉
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
- 新建状态的线程才能被启动
- 加入线程组
- 调用栈中的方法启动
- 启动失败就会从线程组中删除这个线程
2.wait和sleep
查看图还会发现有park和unpark方法,这两个方法属于java.util.concurrent.locks.LockSupport,在这里不做描述
- wait方法属于Object的方法,sleep方法是属于Thread的
- wait方法会主动施放monitor的锁,而sleep不会
public class demo01 {
public static class thread1 extends Thread{
private String name;
public thread1(String name) {
this.name=name;
}
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (thread1.class) {
int i=0;
while(true) {
if(i==50) {
try {
thread1.class.wait();
// Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(name);
i++;
}
}
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Thread t1=new thread1("t1");
Thread t2=new thread1("t2");
t1.start();
t2.start();
}
}
分别打开注释,打开sleep的时候,并不会打印另一个线程名,而打开wait的时候两个线程名都会打印
sleep加上时间,代表休眠指定的时间之后就会重新执行,而wait加上时间之后代表,执行指定的时间之后会重新加入获取锁的队列,获取到锁之后再执行。
3.join方法
父线程必须等待子线程执行完毕或者等待一段时间之后才能继续往下执行。看一下源代码
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
/**
* Waits at most {@code millis} milliseconds plus
* {@code nanos} nanoseconds for this thread to die.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @param nanos
* {@code 0-999999} additional nanoseconds to wait
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative, or the value
* of {@code nanos} is not in the range {@code 0-999999}
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final synchronized void join(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
join(millis);
}
/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
/**
* Tests if this thread is alive. A thread is alive if it has
* been started and has not yet died.
*
* @return <code>true</code> if this thread is alive;
* <code>false</code> otherwise.
*/
public final native boolean isAlive();
可以看到使用的是wait方法,当前线程如果存活,父线程就会等待子线程
4.notify和notifyAll
notify和notifyAll唤醒wait的线程,notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,他们都属于Object的方法,而且要放置在synchronized同步代码块中使用,要不然就会报错,可以看一下
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.zk.threadlook.demo01$ta2.run(demo01.java:49)
at java.lang.Thread.run(Thread.java:748)
下面来举一个进货和销售的例子。
我们一方面要销售产品,如果产品数量为10,那么销售十次之后产品数量就变为了0,这个时候我们就要停止销售,然后赶紧去进货,进货完成后,我们就可以继续进行销售,代码如下
/**
*
* @author 雀跃set
*
*/
public class demo01 {
private static Object obj=new Object();//定义静态全局变量
private static Integer num=10;//存货量
private static Integer count=0;//进货次数
public static class ta1 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
synchronized(obj) {
while(true) {
if(count==1&&num==20) {
System.out.println("进货成功,剩余数量:"+num);
}
num--;
System.out.println("恭喜您下单成功,剩余数量:"+num);
if(num==0) {
try {
if(count==0) {
System.out.println("存货不够了,我要开始进货了");
obj.wait();
}else if(count==1){
System.out.println("已经进过一次货了,欢迎下次再来");
break;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
public static class ta2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (obj) {
try {
System.out.println("进货中");
Thread.sleep(6000);
//给num赋值
num=20;
count=1;
obj.notify();
System.out.println("进货完成,进货数量:"+num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Thread ta1=new Thread(new ta1());
Thread ta2=new Thread(new ta2());
ta1.start();
ta2.start();
}
}
执行结果如下
恭喜您下单成功,剩余数量:9
恭喜您下单成功,剩余数量:8
恭喜您下单成功,剩余数量:7
恭喜您下单成功,剩余数量:6
恭喜您下单成功,剩余数量:5
恭喜您下单成功,剩余数量:4
恭喜您下单成功,剩余数量:3
恭喜您下单成功,剩余数量:2
恭喜您下单成功,剩余数量:1
恭喜您下单成功,剩余数量:0
存货不够了,我要开始进货了
进货中
进货完成,进货数量:20
进货成功,剩余数量:20
恭喜您下单成功,剩余数量:19
恭喜您下单成功,剩余数量:18
恭喜您下单成功,剩余数量:17
恭喜您下单成功,剩余数量:16
恭喜您下单成功,剩余数量:15
恭喜您下单成功,剩余数量:14
恭喜您下单成功,剩余数量:13
恭喜您下单成功,剩余数量:12
恭喜您下单成功,剩余数量:11
恭喜您下单成功,剩余数量:10
恭喜您下单成功,剩余数量:9
恭喜您下单成功,剩余数量:8
恭喜您下单成功,剩余数量:7
恭喜您下单成功,剩余数量:6
恭喜您下单成功,剩余数量:5
恭喜您下单成功,剩余数量:4
恭喜您下单成功,剩余数量:3
恭喜您下单成功,剩余数量:2
恭喜您下单成功,剩余数量:1
恭喜您下单成功,剩余数量:0
已经进过一次货了,欢迎下次再来
5.yield 线程让步
释放锁,把锁释放出来,让自己或者其他线程获取到锁。
结束语
有误之处请您指出,谢谢谢谢