大神原文链接
Thread和Runnable的介绍
Runnable是一个接口里面只有一个方法
public interface Runnable {
public abstractvoid run();
}
Runnable的作用就是实现多线程,可以使一个类实现这个接口,比如A类实现了Runnable,然后new Thread(new A)方式创建线程
Thread是一个实现了Runnable接口的类
public class Thread implements Runnable {
}
也是用于实现多线程
Threa和Runnable的异同
相同:Thread和Runnable都是实现多线程的方式
不同:
Thread是类,Runnable是接口,Thread都是实现了Runnable接口的类,我们知道Java只能继承一个类,但是却能实现多个接口,Runnable有更好的扩展性
Runnable还可以实现资源的共享,即一个Runnable对象,多个Thread共享
通常通过实现Runnable来实现多线程
Thread和Runnable多线程的实例
Thread的多线程实例:
public class MyThread extends Thread { private int ticket = 10; @Override public void run() { for (int i = 0; i < 100; i++) { if (ticket > 0) { System.out.println(this.getName() + "卖票" + ticket--); }}}}
public class Main { public static void main(String[] args) throws InterruptedException { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start();}}
输出:
Thread-2卖票10
Thread-1卖票10
Thread-0卖票10
Thread-1卖票9
Thread-1卖票8
Thread-2卖票9
Thread-1卖票7
Thread-0卖票9
Thread-1卖票6
Thread-2卖票8
Thread-1卖票5
Thread-0卖票8
Thread-1卖票4
Thread-2卖票7
Thread-1卖票3
Thread-0卖票7
Thread-1卖票2
Thread-2卖票6
Thread-1卖票1
Thread-0卖票6
Thread-0卖票5
Thread-0卖票4
Thread-0卖票3
Thread-2卖票5
Thread-0卖票2
Thread-2卖票4
Thread-0卖票1
Thread-2卖票3
Thread-2卖票2
Thread-2卖票1
结果说明:
MyThread继承Thread,它是自定义线程,每个MyThread对象的线程都会卖出十张票
Main主线程创建了3个MyThread线程,每个线程卖出了10张票
Runnable多线程实例
public class MyRunnable implements Runnable { private int ticket = 10; @Override public void run() { for (int i = 0; i < 100; i++) { if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "卖票" + ticket--); } } } }
public class Main { public static void main(String[] args) throws InterruptedException { MyRunnable myRunnable = new MyRunnable(); Thread t1 = new Thread(myRunnable); Thread t2 = new Thread(myRunnable); Thread t3 = new Thread(myRunnable); t1.start(); t2.start(); t3.start(); } }
输出:
Thread-0卖票9
Thread-0卖票7
Thread-0卖票6
Thread-0卖票5
Thread-0卖票4
Thread-2卖票8
Thread-1卖票10
Thread-2卖票2
Thread-0卖票3
Thread-1卖票1
结果说明:
MyRunnable实现接口Runnable,在main线程中创建了三个线程,三个线程都是基于myRunnable对象创建的,三个线程一共卖了10张票,说明了三个线程共享了MyRunnable接口
上面两个例子:三个相同的job在三个线程中跑,一个job在三个线程中跑
start()和run()区别
start()是开始一个新线程,新线程会执行run()方法,start()不能被多次执行
run()只是一个普通的方法,可以被重复调用,如果单纯地调用run方法,并不会创建新的线程,只会在当前的线程中执行run()这个方法
public class Main { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); myThread.run(); myThread.start(); } }
public class MyThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + " run"); } }
输出:
main run
Thread-0 run
结果说明:
Thread.currentThread().getName()会得到当前在cpu运行的线程的名字,myThread.run()是在主线程main中调用的,该run方法会直接运行在主线程上,myThread.start(),会启动线程myThread,启动之后会调用run()方法,run()在线程myThread()中运行
public synchronized void start() { * * A zero status value corresponds to state "NEW".
0状态对应着“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();
Synchronized关键字
Synchronized的原理:
在Java中,每一个对象都有且只有一个同步锁,这也就是说,同步锁是依赖于对象而存在的
当我们调用某个对象的有synchronized关键字修饰的方法或者代码块的时候,我们就获取了该对象的同步锁
不同线程对同步锁的访问是互斥的,也就是说在某一个时间点线程A调用了对象C的synchronized修饰的方法,那么线程B就不能够在这个时间点上调用这个方法,线程B会陷入阻塞状态,直到线程A把对象C的同步锁释放了,它才会有机会获取这个锁,继而调用这个方法
Synchronized的基本规则
①当有一个线程已经正在访问某个对象的synchronized方法/代码块的时候,其他线程访问这个对象的这个synchronized方法/代码块将会被阻塞
②当有一个线程已经正在访问某个对象的synchronized方法/代码块的时候,其他线程可以访问这个对象的其他非同步的方法/代码块
③当有一个线程已经正在访问某个对象的synchronized方法/代码块的时候,其他线程访问该对象的其他synchronized方法/代码块同样也是hi阻塞
Demo1.1:
public class Main { public static void main(String[] args) throws InterruptedException { MyRunnable myRunnable = new MyRunnable();//拥有同步锁的对象 Thread myThread1 = new Thread(myRunnable); Thread myThread2 = new Thread(myRunnable); myThread1.setName("线程1"); myThread2.setName("线程2"); myThread1.start(); myThread2.start(); } }
public class MyRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + " run"); //执行到这里synchronized (this) ,线程得到这个对象的锁就可以继续跑下面的代码,得不到就会阻塞,等待这个对象的锁的释放 synchronized (this) { for (int i = 0; i < 4; i++) { try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "---" + System.currentTimeMillis()); } } System.out.println(Thread.currentThread().getName() + "结束了"); } }
输出:
线程1 run
线程2 run
线程1---1473239410026
线程1---1473239411026
线程1---1473239412027
线程1---1473239413027
线程1结束了
线程2---1473239414028
线程2---1473239415028
线程2---1473239416029
线程2---1473239417030
线程2结束了结果说明:
线程1 和线程2 都在启动后想访问对象myRunnable对象的synchronized方法,但是因为线程1比线程2更快的得到了cpu的调度,使线程1先得到了myRunnable对象的同步锁,后来即使线程2也得到了cpu的调度,但是得不到myRunnable对象的同步锁,进而马上陷入了阻塞状态,直到线程1执行完释放了myRunnable对象的同步锁,线程2才得以继续得到cpu调度运行
Demo1.2
public class MyThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + " run"); //执行到这里synchronized (this) ,线程得到这个对象的锁就可以继续跑下面的代码,得不到就会阻塞,等待这个对象的锁的释放 synchronized (this) { for (int i = 0; i < 4; i++) { try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "---" + System.currentTimeMillis()); } } System.out.println(Thread.currentThread().getName() + "结束了"); } }
public class Main { public static void main(String[] args) throws InterruptedException { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.setName("线程1"); myThread2.setName("线程2"); myThread1.start(); myThread2.start(); } }
输出:
线程1 run
线程2 run
线程1---1473239664685
线程2---1473239664685
线程1---1473239665686
线程2---1473239665686
线程2---1473239666686
线程1---1473239666686
线程1---1473239667687
线程1结束了
线程2---1473239667687
线程2结束了
结果说明:
如果你对这个结果并没有感到惊讶,那么你应该是理解了synchronized了
在main线程中我们分别创建了两个线程,线程1和线程2,这是两个不一样的对象,而代码中synchronized(this),中的this指的是当前的类对象,作用是获取这个对象的同步锁,因为线程1和线程2是两个不一样的对象,所有执行到synchronized(this)的时候,这两个线程获取到的是两个不一样的同步锁,而demo1.1中,synchronized(this)的this是指myRunnable对象,又因为demo1.1中的线程1和线程2共同使用着myRunnable这个对象,所有一个线程获取了这个对象的同步锁,那么别的线程就会阻塞等待
Demo2
public class Demo2 { public synchronized void synchronizedMethod() { for (int i = 0; i < 4; i++) { System.out.println(Thread.currentThread().getName()+"在synchronized方法中"); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public void noSynchronizedMethod() { for (int i = 0; i < 4; i++) { System.out.println(Thread.currentThread().getName()+"在noSynchronized方法中"); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class MyThread extends Thread { Demo2 demo2; public MyThread(Demo2 demo2) { this.demo2 = demo2; } @Override public void run() { demo2.synchronizedMethod(); } } public class MyRunnable implements Runnable { Demo2 demo2; public MyRunnable(Demo2 demo2) { this.demo2 = demo2; } @Override public void run() { demo2.noSynchronizedMethod(); } } public class Main { public static void main(String[] args) throws InterruptedException { Demo2 demo2 = new Demo2(); MyThread myThread1 = new MyThread(demo2); MyRunnable myRunnable = new MyRunnable(demo2); Thread myThread2 = new Thread(myRunnable); myThread1.setName("线程1"); myThread2.setName("线程2"); myThread1.start(); myThread2.start(); } }
输出:
线程1在synchronized方法中
线程2在noSynchronized方法中
线程1在synchronized方法中
线程2在noSynchronized方法中
线程1在synchronized方法中
线程2在noSynchronized方法中
线程1在synchronized方法中
线程2在noSynchronized方法中
结果说明:
线程1调用了demo2的synchronized方法,而线程2调用了demo2的noSynchronized方法,虽然线程1在运行的时候已经获取了demo2的同步锁,但是线程2调用的方法不是synchronized方法,所以没有用到同步锁,
Demo3
public class Demo2 {
publicsynchronized void synchronizedMethod1() {
for (int i = 0; i < 4; i++) {
System.out.println(Thread.currentThread().getName()+"synchronizedMethod1");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
publicsynchronized void synchronizedMethod2() {
for (int i = 0; i < 4; i++) {
System.out.println(Thread.currentThread().getName()+"synchronizedMethod2");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MyThread extends Thread {
Demo2 demo2;
public MyThread(Demo2 demo2) {
this.demo2 = demo2;
}
@Override
public void run() {
demo2.synchronizedMethod1();
}
}
public class MyRunnable implements Runnable {
Demo2 demo2;
public MyRunnable(Demo2 demo2) {
this.demo2 = demo2;
}
@Override
public void run() {
demo2.synchronizedMethod2();
}
}
public class Main {
public staticvoid main(String[] args) throws InterruptedException{
Demo2 demo2 = new Demo2();
MyThreadmyThread1 = new MyThread(demo2);
MyRunnable myRunnable= new MyRunnable(demo2);
Thread myThread2= new Thread(myRunnable);
myThread1.setName("线程1");
myThread2.setName("线程2");
myThread1.start();
myThread2.start();
}
}
输出
线程2synchronizedMethod2
线程2synchronizedMethod2
线程2synchronizedMethod2
线程2synchronizedMethod2
线程1synchronizedMethod1
线程1synchronizedMethod1
线程1synchronizedMethod1
线程1synchronizedMethod1
结果说明:
线程1 和线程2 都有同一个对象demo2,类Demo2中有两个同步方法,线程1和线程2分别调用两个不同的同步方法,但是因为一个对象只有一个同步锁,所以这次线程2 先得到cpu的调度,先得到了demo2对象的同步锁,当线程1运行时得不到demo2对象的同步锁,所以陷入了阻塞
Synchronized方法和Synchronized的代码块
Synchronized方法是指用synchronized修饰的方法,Synchronized代码块是指用synchronized修饰的代码块。
// synchronized 方法
publicsynchronized void method1() {
}
publicsynchronized void method2() {
// synchronized 代码块,this代表这个类的对象
synchronized (this) {
}
String s = "sdf";
//s 的对象
synchronized (s) {
}
}
实例锁和全局锁
实例锁:锁存在于一个对象的身上,实例锁对应的就是synchronized关键字
全局锁:该锁针对的是类,无论这个类有多少对象,线程都共享这个锁,对应的是 static synchronized 关键字
class Demo2
public synchronized void objectLock1() {
for (int i = 0; i < 4; i++) {
System.out.println(Thread.currentThread().getName() + " objectLock1");
}
}
public synchronized void objectLock2() {
for (int i = 0; i < 4; i++) {
System.out.println(Thread.currentThread().getName() + " objectLock2");
}
}
public static synchronized void classLock1() {
for (int i = 0; i < 4; i++) {
System.out.println(Thread.currentThread().getName() + " classLock1");
}
}
public static synchronized void classLock2() {
for (int i = 0; i < 4; i++) {
System.out.println(Thread.currentThread().getName() + " classLock2");
}
}
假设x,y是类Demo2的两个对象,
①x.objectLock1 和 x.objectLock2不可以,一个对象一个锁
②x. objectLock1 和y. objectLock1可以,两个不同的对象
③x. objectLock1 和 y.classLock1可以,一个对象锁一个全局锁
④Demo2.classLock1 和 y.classLock2不可以,都是全局锁
⑤x.classLock1 和 y.classLock2 不可以,都是全局锁
线程的等待wait和唤醒notify
Wait notify notifyAll方法介绍
Wait
假如当前线程获得某个对象的同步锁,那么可以调用这个对象的wait(),使当前的线程释放该对象的同步锁,并且当前线程陷入等待阻塞状态,只有当这个对象调用notify/notifyAll方法的时候才可以使那个线程进入就绪状态
例子
public class Main {
public static voidmain(String[] args) throws InterruptedException{
String s = "adf";
synchronized (s) {
s.wait();
}
System.out.println("deng");
}
}
main线程一直处于阻塞状态,没法结束,deng 也没法打印出来,因为main线程在执行完s.wait就一直处于阻塞,它必须要对象s用notify/notifyAll才能使这个线程继续就绪状态。当你调用对象的wait的方法的时候,你当前这个线程必须拥有这个对象的同步锁,所以s.wait
方法要在synchronized中调用,当调用wait方法之后,当前的线程就会释放了这个对象的锁
public class Main {
public staticvoid main(String[] args) throws InterruptedException{
String s = "adf";
new MyThread().start();
synchronized (s) {
System.out.println("main线程进入等待"); s.wait(); } System.out.println("deng"); } } public class MyThread extends Thread { @Override public void run() { String s = "adf";//与main线程中的对象s是同一个对象 synchronized (s) { for (int i = 0; i < 4; i++) { System.out.println("main线程还没notify"); } s.notify(); System.out.println("main线程notify了"); } } }
输出:
main线程进入等待
main线程还没notify
main线程还没notify
main线程还没notify
main线程还没notify
main线程notify了
deng
在main线程中新建了一个线程,并且启动了它,然后main线程得到了对象s的锁,执行wait方法,进入等待,并且释放了s对象的锁,然后另一个线程才得到了s的锁,执行4次循环后,调用对象s.notify方法,因为在s对象上只有一个一个等待的线程,所以main被唤醒,进入就绪,然后得到cpu调度得以执行下去
wait(long timeout)
public class Main {
public staticvoid main(String[] args) throws InterruptedException{
String s = "adf";
synchronized (s) {
System.out.println("main线程进入等待"+" 时间是"+ System.currentTimeMillis());
s.wait(1000);
System.out.println("main线程结束等待"+" 时间是"+ System.currentTimeMillis());
}
}
}
输出:
main线程进入等待 时间是1473308280606
main线程结束等待 时间是1473308281607
结果说明:
这次调用wait(long timeout),跟wait()方法差不多,就是多了一个,当这个线程陷入等待的时间大于这个timeout这个时间的时候,当这个线程再次获得这个对象的锁的时候,它会继续执行刚刚没执行的代码
public class Main {
public staticvoid main(String[] args) throws InterruptedException{
String s = "adf";
new MyThread().start();
synchronized (s) {
System.out.println("main线程进入等待"+" 时间是"+ System.currentTimeMillis());
s.wait(1000);
System.out.println("main线程结束等待"+" 时间是"+ System.currentTimeMillis());
}
}
}
public class MyThread extends Thread {
@Override
public void run() {
String s = "adf";//与main线程中的对象s是同一个对象
synchronized (s) {
while (true) {
}
}
}
}
输出:
main线程进入等待 时间是1473309592724
他是永远不会输出main线程结束等待 时间是xxxx 的,因为另外的一个线程一直拿着对象s的锁不放,那么main线程得不到这个锁,就会陷入阻塞等待
public class Main {
public staticvoid main(String[] args) throws InterruptedException{
String s = "sdf";
MyThreadmyThread1 = new MyThread();
MyThreadmyThread2 = new MyThread();
MyThreadmyThread3 = new MyThread();
myThread1.setName("线程1");
myThread2.setName("线程2");
myThread3.setName("线程3");
myThread1.start();
myThread2.start();
myThread3.start();
System.out.println(System.currentTimeMillis());
Thread.currentThread().sleep(3000);
System.out.println(System.currentTimeMillis());
synchronized (s) {
s.notifyAll();
}
}
}
public class MyThread extends Thread {
String s = "sdf";
@Override
public void run() {
synchronized (s) {
try {
System.out.println("我是"+Thread.currentThread().getName());
s.wait();
System.out.println(Thread.currentThread().getName()+"解放");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出:
1473327126143
我是线程1
我是线程2
我是线程3
1473327129143
线程3解放
线程2解放
线程1解放
结果说明:
在main线程中创建了三个线程,这三个线程启动之后就会得到对象s的同步锁,然后调用wait,使自己陷入等待状态,然后main线程在sleep(3000)之后就会获取对象s的同步锁然后调用notifyAll,把在对象s上阻塞的所有线程唤醒,进入到就绪状态,等待cpu的调度
线程的让步yield()
yield()的作用就是让步,让当前的线程从运行状态变为就绪状态,从而让其他的相同优先级的线程可以有机会得到cpu的调度。但是不一定其他的线程会得到调度,这主要还是要看cpu想要调度哪个线程
public class Main {
public staticvoid main(String[] args) throws InterruptedException{
MyThread myThread = new MyThread();
myThread.setName("线程1");
myThread.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" run"+ i);
if (i % 4 == 0) {
yield();
}
}
}
}
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" run"+ i);
if (i % 4 == 0) {
yield();
}}}}
输出:
main run 0
线程1 run 0
main run 1
线程1 run 1
线程1 run 2
main run 2
线程1 run 3
main run 3
线程1 run 4
main run 4
线程1 run 5
main run 5
线程1 run 6
main run 6
线程1 run 7
main run 7
线程1 run 8
main run 8
线程1 run 9
main run 9
在main线程创建一个线程1,每当main线程或者线程1 for循环的i%4为0的时候,调用yield(),让当前线程由运行状态变为就绪状态,当线程1和main线程都是就绪状态的时候,哪个能先运行,是取决于cpu的
yield 和 wait的区别
yield:是让当前运行的线程由运行状态变为就绪状态,不会释放同步锁,只是放弃cpu这个资源
wait:是让当前线程由运行状态变为阻塞等待状态,会释放该线程手上的同步锁
就绪状态:在等待cpu的调度,也就是说,这个线程除了等待cpu资源之外,其他的资源已经到手了
阻塞状态:除了在等待cpu资源之外还在等待其他资源,如某个对象的同步锁
线程休眠sleep
Sleep就是使当前的线程由运行状态变为阻塞休眠状态,线程等于/大于给定的时间就会结束休眠,有休眠阻塞状态变为就绪状态
public class Main {
public staticvoid main(String[] args) throws InterruptedException{
for (int i = 0; i < 4; i++) {
System.out.println(System.currentTimeMillis());
Thread.currentThread().sleep(1000);
}
}}
输出:
1473343174520
1473343175521
1473343176522
1473343177522
结果说明:
For循环中,每执行一次就会使main线程休眠1秒,1秒之后才有阻塞状态变为就绪状态然后cpu调度再次变为运行状态
Wait和sleep区别:
Wait 和 sleep都是使当前的线程由运行状态变为阻塞状态,区别就是是否释放掉手上的对象的同步锁,wait释放,而sleep是不释放的
public class Main {
public staticvoid main(String[] args) throws InterruptedException{
String s = "asd";
MyThreadmyThread1 = new MyThread();
MyThreadmyThread2 = new MyThread();
myThread1.start();
myThread2.start();
synchronized (s) {
for (int i = 0; i < 5; i++) {
System.out.println( Thread.currentThread().getName()+" 我sleep,但是不释放对象的同步锁");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class MyThread extends Thread {
String s = "asd";
@Override
public void run() {
synchronized (s) {
for (int i = 0; i < 5; i++) {
System.out.println( getName()+" 我sleep,但是不释放对象的同步锁");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
输出:
main 我sleep,但是不释放对象的同步锁
main 我sleep,但是不释放对象的同步锁
main 我sleep,但是不释放对象的同步锁
main 我sleep,但是不释放对象的同步锁
main 我sleep,但是不释放对象的同步锁
Thread-0 我sleep,但是不释放对象的同步锁
Thread-0 我sleep,但是不释放对象的同步锁
Thread-0 我sleep,但是不释放对象的同步锁
Thread-0 我sleep,但是不释放对象的同步锁
Thread-0 我sleep,但是不释放对象的同步锁
Thread-1 我sleep,但是不释放对象的同步锁
Thread-1 我sleep,但是不释放对象的同步锁
Thread-1 我sleep,但是不释放对象的同步锁
Thread-1 我sleep,但是不释放对象的同步锁
Thread-1 我sleep,但是不释放对象的同步锁
结果说明:
main线程中创建另外两个线程,三个线程都想拥有对象s的同步锁,先是main得到然后是Thread-0其次是Thread-1,虽然在for循环中sleep,但是可以看到线程sleep是不会释放同步锁的
join()
作用:使当前的线程陷入阻塞直到让它陷入阻塞的线程结束,懵逼…..
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
if (i == 999) {
System.out.println(getName()+"执行完毕");
}
}
}
}
public class MyRunnable implements Runnable {
MyThread myThread;
public MyRunnable(MyThread myThread) {
this.myThread = myThread;
}
@Override
public void run() {
try {
myThread.join();
System.out.println("等待myThread的结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Main {
public staticvoid main(String[] args) throws InterruptedException{
MyThread myThread = new MyThread();
myThread.start();
MyRunnablemyRunnable = new MyRunnable(myThread);
new Thread(myRunnable).start();
}
}
输出:
Thread-0执行完毕
等待myThread的结束
结果说明:
在MyRunnable中我们myThread.join();使这个线程要等到myThread这个线程结束之后才能执行,有点需要注意的是,myThread最好是在myRunnable之前start
能Get到吗?
源码分析:
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;
}
}
}
分析:
Join(); 这个函数最终会调用这个函数,传入的参数为0而已
While循环会判断myThread的状态,如果是isAlive就进入循环,所以这里myThread要比myRunnable先start,然后如果这个myThread是alive的话,那么就会让当前的线程进入阻塞,也就是说myRunnable线程进入阻塞
Interrupt()和线程的结束
/**
* Interrupts this thread.
*
* <p> Unless the current thread is interrupting itself, which is
* always permitted, the {@link #checkAccess()checkAccess} method
* of this thread is invoked, which maycause a {@link
* SecurityException} to be thrown.
*除非是线程之间interrupt自己,否则的话都会调用该线程的checkAccess()
*方法的,并且会抛出SecurityException0
*
* <p> If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long)wait(long)}, or {@link
* Object#wait(long, int) wait(long,int)} methods of the {@link Object}
* class, or of the {@link #join()},{@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)},or {@link #sleep(long, int)},
* methods of this class, then itsinterrupt status will be cleared and it
* will receive an {@link InterruptedException}.
*
假如这个线程因为wait 或者join sleep 方法而陷入阻塞的,调用interrupt方法的话会让interrupt标志位变回false,并且这个线程会接受到一个InterruptedException
* <p> If this thread is blocked in an I/O operation upon an {@link
*java.nio.channels.InterruptibleChannel InterruptibleChannel}
* then the channel will be closed, thethread's interrupt
* status will be set, and the threadwill receive a {@link
*java.nio.channels.ClosedByInterruptException}.
*如果这个线程在InterruptibleChannel上I.O操作的话,这个管道就会被关** *闭,interrupt标志位设置为true,这个线程将会收到一个*ClosedByInterruptException
* <p> If this thread is blocked in a {@link java.nio.channels.Selector}
* then the thread's interrupt statuswill be set and it will return
* immediately from the selectionoperation, possibly with a non-zero
* value, just as if the selector's {@link
* java.nio.channels.Selector#wakeupwakeup} method were invoked.
*如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。
* <p> If none of the previous conditions hold then this thread's interrupt
* status will be set. </p>如果不属于上面所说的情况,这个interrupt标志位就会设置为true
*
* <p> Interrupting a thread that is not alive need not have any effect.
*
* @throws SecurityException
* if the current thread cannot modifythis thread
*中断一个“已终止的线程”不会产生任何操作。
* @revised 6.0
* @spec JSR-51
*/
终止方式:
因为stop和suspend方法对原子操作安全系问题,已经废弃
public class MyThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName()+"进入while"); System.out.println("isInterrupted :"+ isInterrupted()); while (!isInterrupted()) { } System.out.println("isInterrupted :"+ isInterrupted()); System.out.println(Thread.currentThread().getName() + " 我要结束了"); } }
public class Main { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); myThread.setName("线程1"); myThread.start(); for (int i = 0; i < 4; i++) { System.out.println("我是" + Thread.currentThread().getName()); Thread.currentThread().sleep(1000); } myThread.interrupt(); } }
输出:
我是main
线程1进入while
isInterrupted :false
我是main
我是main
我是main
isInterrupted :true
线程1 我要结束了
结果说明:
在main线程中创建并且启动了线程1,线程1因为interrupt标志位默认是false,所以一直循环,直到main线程调用interrupt,使线程1的interrupt标志位设置为true,然后线程1的循环结束
public class MyThread extends Thread { volatile boolean flag; public void setFlag(boolean flag) { this.flag = flag; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "进入while"); System.out.println("flag :" + flag); while (!flag) { } System.out.println("flag :" + flag); System.out.println(Thread.currentThread().getName() + " 我要结束了"); } }
public class Main { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); myThread.setName("线程1"); myThread.start(); for (int i = 0; i < 4; i++) { System.out.println("我是" + Thread.currentThread().getName()); Thread.currentThread().sleep(1000); } myThread.setFlag(true); } }
输出:
我是main
线程1进入while
flag :false
我是main
我是main
我是main
flag :true
线程1 我要结束了
结果说明:
通过设置一个flag来中断线程,本质上和interrupt标志位是一样的,至于volatile关键字,你可以把它想象成synchronized的缩小版,也是用于实现同步的,使使用flag这个变量的时候能使用到最新的值,而不是缓存中的值
当你想要interrupt的线程处于阻塞状态那么那个你想要阻塞的线程接收到你发出的interrupt之后,会接收到一个InterruptedException ,
public class Main {
public staticvoid main(String[] args) throws InterruptedException{
MyThread myThread = new MyThread();
myThread.setName("线程1");
myThread.start();
for (int i = 0; i < 4; i++) {
System.out.println("我是" + Thread.currentThread().getName());
Thread.currentThread().sleep(1000);
}
myThread.interrupt();
}
}
public class MyThread extends Thread {
volatile booleanflag;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "进入while");
while (!isInterrupted()){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
System.out.println(Thread.currentThread().getName() + " 我要结束了");
}
}
输出:
我是main
线程1进入while
我是main
我是main
我是main
java.lang.InterruptedException: sleepinterrupted
线程1 我要结束了
atjava.lang.Thread.sleep(Native Method)
atcom.company.MyThread.run(MyThread.java:23)
结果说明:
当在main线程使线程1中断的时候,线程1处于阻塞的状态,所以会接收到一个InterruptedException,然后因为这个try/catch语句块是在while循环内的,所以要break一下,跳出循环
当然也可以将while循环放进try/catch中
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "进入while");
try {
while (!isInterrupted()) {
sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 我要结束了");
}
结果还是一样的
isInterrupt和interrupted的区别:
都是返回当前的线程的interrupt标志位的状态,但是interrupted还会将标志位clear(设置为false)
线程的优先级和守护线程
* Every thread has a priority. Threads with higher priority are * executed in preference to threads with lower priority. Each thread * may or may not also be marked as a daemon. When code running in * some thread creates a new <code>Thread</code> object, the new * thread has its priority initially set equal to the priority of the * creating thread, and is a daemon thread if and only if the * creating thread is a daemon.
每一个线程都有一个优先级,优先级高的线程优先于优先级低的线程执行,每一个线程可以设置为师守护线程也可以不,你在一个线程中创建另一个线程,那么新建的线程初始化值是等于那个创建它的线程的值的,优先级,是否是守护线程。
<p> * When a Java Virtual Machine starts up, there is usually a single * non-daemon thread (which typically calls the method named * <code>main</code> of some designated class). The Java Virtual * Machine continues to execute threads until either of the following * occurs: * <ul> * <li>The <code>exit</code> method of class <code>Runtime</code> has been * called and the security manager has permitted the exit operation * to take place. * <li>All threads that are not daemon threads have died, either by * returning from the call to the <code>run</code> method or by * throwing an exception that propagates beyond the <code>run</code> * method. * </ul> * <p>
当Java虚拟机启动的时候,通常有一个非守护线程跑起来(这个线程就是调用main方法的)jvm会一直执行这个线程,直到下面的情况出现
① Runtime类中的exit方法调用并且安全管理器允许exit方法执行
② 所有的非daemon线程结束,要不就是从润方法中执行完返回,要不就是在run方法中throw exception
public class MyThread extends Thread { public MyThread(String name) { super(name); } @Override public void run() { System.out.println(getName() + " " + isDaemon()); System.out.println(getName() + " " + getPriority()); } }
public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } } }
public class Main { public static void main(String[] args) throws InterruptedException { System.out.println(Thread.currentThread().isDaemon()); System.out.println(Thread.currentThread().getPriority()); new MyThread("线程1").start(); Thread thread = new Thread(new MyRunnable()); thread.setDaemon(true); thread.setName("daemon"); thread.start(); } }
输出:
false
5
线程1 false
线程1 5
daemon 0
daemon 1
daemon 2
daemon 3
daemon 4
daemon 5
daemon 6
daemon 7
daemon 8
结果说明:
当线程1和main线程结束后,JVM就会结束,所以不会等到daemon线程结束后才退出
知识点回顾总结
只有wait()才会释放掉对象的锁,其实这个很好理解,因为wait方法是Object类方法,wait方法的作用就是使当前的线程就入到一种等待的状态,如果这个对象不调用notify/notifyAll,或者其他线程interrupt那个线程,那么这个线程永远不会从等待block中出来,那么就永远谈不上调度这个线程缺乏哪些资源。当你从等待block/因为synchronized进入到锁定block的话,那么此时的线程是缺乏资源,这个资源就是某个对象的同步锁,当它得到这个同步锁之后,进入到就绪状态,线程唯一现在缺乏的就是cpu资源,也就是需要cpu的调度。当线程从运行状态到就绪状态的时候,也就是调用yield方法的时候,这个线程只是放弃了cpu的这个资源,而从运行状态到block状态的话,是不会释放同步锁的,而由block到就绪状态除了join结束,和sleep结束,interrupt其实也是可以的,而这里的I/O就不是很好理解,
生产者消费者问题
这是一个非常经典的多线程问题,涉及到生产者,消费者,仓库,之间的关系如下
①当仓库为空的时候,消费者不能消耗,等生产者生产了才可以有得吃
②当仓库满的时候,生产者不能再生产,等消费者消费之后才可以继续生产
③消费者发现仓库为空的时候会通知生产者去生产
④生产者生产出产品的时候会通知消费者有产品可以消费
⑤仓库在某一时刻只能有一类人进入,要不是生产者要不是消费者
public class Depot { private int capacity;//仓库总容量 private int inventory;//仓库中的库存 public Depot(int capacity) { this.capacity = capacity; this.inventory = 0; } public synchronized void produce(int produceCount) { int stay = produceCount; try { //生产的数目可能分多次 while (stay > 0) { while (inventory >= capacity) {//仓库满了,等待消费者消费 this.wait(); } //一次性生产多少,剩余容量还是stay这个值 int inc = inventory + stay > capacity ? capacity - inventory : stay; inventory += inc;//余量+ stay -= inc;//生产数目- notifyAll();//通知消费者消费 System.out.println(Thread.currentThread().getName() + " 生产了" + inc); System.out.println(Thread.currentThread().getName() + " 还剩下" + inventory); } } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void comsume(int consumeCount) { int take = consumeCount; try { //一次性拿的数目可能分多次 while (take > 0) { while (inventory <= 0) {//没存货了,通知生产者 notifyAll(); wait(); } int takePerTime = take > inventory ? inventory : take; inventory -= takePerTime; take -= takePerTime; System.out.println(Thread.currentThread().getName() + " 消费了" + takePerTime); System.out.println(Thread.currentThread().getName() + " 还剩下" + inventory); } } catch (InterruptedException e) { e.printStackTrace(); } } public int getCapacity() { return capacity; } public int getInventory() { return inventory; } } class Consume { private Depot depot; public Consume(Depot depot) { this.depot = depot; } public void consumeProduct(int count) { new Thread(new Runnable() { @Override public void run() { depot.comsume(count); } }).start(); } } class Producer { Depot depot; public Producer(Depot depot) { this.depot = depot; } public void produce(int count) { new Thread(new Runnable() { @Override public void run() { depot.produce(count); } }).start(); } }
public class Main { public static void main(String[] args) { Depot depot = new Depot(100); Consume consume = new Consume(depot); Producer producer = new Producer(depot); producer.produce(60); producer.produce(120); consume.consumeProduct(90); consume.consumeProduct(150); producer.produce(110); } }
输出:
Thread-1 生产了100
Thread-1 还剩下100
Thread-2 消费了90
Thread-2 还剩下10
Thread-4 生产了90
Thread-4 还剩下100
Thread-3 消费了100
Thread-3 还剩下0
Thread-4 生产了20
Thread-4 还剩下20
Thread-0 生产了60
Thread-0 还剩下80
Thread-1 生产了20
Thread-1 还剩下100
Thread-3 消费了50
Thread-3 还剩下50
结果说明:
每个生产者和消费者都有depot实例,能对其进行生产和消费,当消费者发现仓库没货的时候会通知生产者去生产,生产者的每一个线程就像一条生产线,由cpu决定哪条生产线启动,而每条生产线生产的数目固定于生产者的设定每当一天生产线生产完毕之后就会通知消费者去消费
谢谢大神原文链接