类似于操作系统中的概念,每个线程都是共享其隶属的进程空间,如堆、方法区,
但是同时,每一个线程还是有一点属于自己的资源:程序计数器以及方法栈。
在java的线程设计中,start方法只是使得线程处于就绪状态,只有当执行了run方法才获取cpu控制器、才处于真正的执行状态。
但是一般来说,线程之间都是时间片调度,所以即使run(或者call)方法与start方法是绑定一起执行的,但是run方法一般都是滞后于start方法。
java中的PV操作:
最基本的是使用notify、synchronized以及wait方法来实现。
其中,synchronized关键字可以加在实体对象上(类似try),也可以加在方法上:
①
synchronized(实体){
if(条件不足)
实体.wait();
}
②
synchronized void method(){
do something
}
当一个实体处于wait时,一般情况下都是需要其他实体通过调用notifyAll方法通知而解除wait状态
在方法上加synchronized关键字时,不需要手动调用wait,也不需要其他实体调用notify,jvm自动调用。
以生产者消费者为例:
现在有一个生产者消费者公用队列queue
生产者:
synchronized(queue){
while(queue是满的){
//先放弃对queue的锁。然后:
queue.wait();
}
//此时队列不满
queue.add(obj); //生产
queue.notifyAll() //发出通知。通知所有的被处于wait态的实体。相当于V操作
}
消费者:
synchronized(queue){
while(queue是空的){
//先放弃对queue的锁。然后线程本身等待
queue.wait();
}
//队列不为空
queue.get(obj); //消费
queue.notifyAll() //发出通知
}
当线程被wait()阻塞后,只是剩下wait()语句出现之后的代码段还未执行。
当被唤醒时,线程会从wait()语句下一条语句开始执行。
也就是说线程被阻塞的时候,同时保存了执行点情况
可也是为什么,notify/notifyAll需要搭配synchronized一起使用
线程的interrupt()方法与isInterrupted()方法:
首先,interrupt()方法内部是通过调用isInterrupted()方法实现,并且:
如果当前线程是阻塞的,调用isInterrupted()方法时理所当然是返回:true;反之false。
而如果当前线程是阻塞的,而且调用interrupt()方法,则会返回:false。
因为:interrupt()方法会改变线程阻塞状态:阻塞态调用则转为就绪唤醒态;反之亦然。即:
interrupt()方法会清除中断标志。
小技巧:
在命令行中使用:jps 命令查看当前在jvm中运行的进程(一般是用户进程)。
普通的ThreadLocal与inheritableThreadLocal之间的异同:
首先,两者都是线程内部的域,每一个线程都有,且私有。
这个私有、同级线程之间都不一样是通过设置值的时候实现的:
通过源码以及继承关系知道,不管是ThreadLocal,还是inheritableThreadLocal,都是一个ThreadLocals的映射,
而这个ThreadLocals底层是由一个特殊哈希函数的HashMap实现的。
而在设置值的时候,key是传入的Thread的引用,value是泛型T。
所以,通过由这个引用设置的key,从而达到私有的目的,这也是ThreadLocal的思路。
但是,如果想要使得子线程像共享父进程(线程)资源那样,共享父线程的ThreadLocals,则需要通过inheritableThreadLocal来实现。
其实inheritableThreadLocal与ThreadLocal的差距就在线程创建的本身:
通过阅读源码可以看到,创建子线程的时候,通过线程的构造函数,先检查父线程的inheritableThreadLocal的有无,
如果不为null,则将子线程的inheritableThreadLocal设置为:
this.inheritableThreadLocal = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals)
其中,parent是父线程。
通过ThreadLocal的静态方法:createInheritedMap()来为子线程的inheritableThreadLocal域赋值,相当于复制。
createInheritedMap()方法:
createInheritedMap(ThreadLocalMap parentMap){
return new ThreadLocalMap(parentMap);
}
可以看出,这种复制是值的复制,而不是引用复制。
第一种:继承Thread类
package createThreadMethod;
//使用继承Thread类的方法实现创建线程的目的。
//优点是:比较简单,可以在自定义Thread子类中添加各种域以及方法。
//缺点是:①执行任务单一。线程本身与线程要执行的任务硬绑定。
// ②无返回值
public class OneThread extends Thread{
@Override
public void run() {
System.out.println("the first method that create the multiThread.....");
}
public static void main(String []args){
Thread oneThread = new OneThread();
oneThread.start();
}
}
第二种:实现Runnable接口
package createThreadMethod;/*
name: TwoThread
user: ly
Date: 2020/5/29
Time: 11:16
*/
//使用实现Runnable接口来创建多线程
//优点为:线程本身与线程执行的任务彻底分离。
//缺点也很明显:无返回值。
public class TwoThread {
public static class RunnableTask implements Runnable{
public void run() {
System.out.println("the second method that create the multiThread.....");
}
}
public static void main(String []args){
Runnable task1 = new RunnableTask();
Runnable task2 = new RunnableTask();
new Thread(task1).start();
new Thread(task2).start();
}
}
第三种:实现callable接口,并使用futureTask实现
package createThreadMethod;/*
name: ThirdThread
user: ly
Date: 2020/5/29
Time: 11:36
*/
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThirdThread{
public static class MyCallableTask implements Callable{
public Object call() throws Exception { //注意第三种方法与前面两种的差别:重写run方法 VS 重写call方法
return "\"the third method that create the multiThread.....\"";
}
}
public static void main(String []args){
//使用FutureTask(与第二种方法--使用Runnable的不同
FutureTask<String> futureTask = new FutureTask<String>(new MyCallableTask());
//先启动。但是此时该线程不一定立即执行
new Thread(futureTask).start();
try{
//此时等待执行,并且从线程执行的任务:MycallableTask中可以看到,线程执行后会返回一个Object,这里定义为String
String result = futureTask.get();
System.out.println(result);
}catch (ExecutionException e){
e.printStackTrace();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
最后是一些java中实现的进程、线程与传统操作系统理论中的差异:
① synchronized与wait(即java中的PV操作)
package createThreadMethod;/*
name: demo01
user: ly
Date: 2020/5/29
Time: 14:08
*/
/测试:synchronized与wait(即java中的PV操作)
public class demo01 {
static volatile Object obj1 = new Object();
static volatile Object obj2 = new Object();
public static void main(String []args) throws InterruptedException{
test4();
}
//会死锁 说明:即使obj2是在obj1内部获取的,当调用obj1.wait()使得obj1的锁释放时,obj2的锁不会也跟随释放,除非手动释放。
public static void test1() throws InterruptedException{
//创建两个线程
Thread threadA = new Thread(new Runnable() {
public void run() {
try{
//先获取obj1
synchronized (obj1){
System.out.println("threadA get the resource1's lock...");
//再尝试获取obj2
synchronized (obj2){
System.out.println("threadA get the resource2'lock...");
//再释放obj1的锁
System.out.println("threadA release the resource1' lock");
obj1.wait();
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
//创建另一个线程
Thread threadB = new Thread(new Runnable() {
public void run() {
try{
//先休眠1s。让threadA先获取object
Thread.sleep(1000);
//再尝试去获取OBJ1
synchronized (obj1){
System.out.println("threadB get the resource1's lock...");
// 拿到object1的锁后
//然后再尝试拿到object2的锁
synchronized (obj2){ //应该是拿不到的。因为threadA没有释放
System.out.println("threadB get the resource2's lock...");
//再释放object1
System.out.println("threadB release the resource1's lock"); //应该是放不了
obj1.wait();
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
//启动两个线程
threadA.start();
threadB.start();
//等待线程结束
threadA.join();
threadB.join();
System.out.println("main thread is over...");
}
//阻塞后中断
public static void test2() throws InterruptedException{
Thread thread = new Thread(new Runnable() {
public void run() {
try{
System.out.println("----begin-----");
synchronized (obj1){
obj1.wait(); //自己阻塞自己
}
System.out.println("----end----");
}catch (InterruptedException e){
System.out.println("interrupt...");
e.printStackTrace();
}
}
});
thread.start();
Thread.sleep(2000);
System.out.println("----begin interrupt sub thread");
thread.interrupt();
System.out.println("----end interrupt sub thread");
}
public static void test3() throws InterruptedException{
Thread threadA = new Thread(new Runnable() {
public void run() {
//先获取obj1
try{
synchronized (obj1){
System.out.println("threadA get the obj1 lock");
Thread.sleep(1000);
synchronized (obj2){
System.out.println("threadA get the obj2 lock");
}
System.out.println("threadA release the obj2 lock");
// obj2.notify();
}
// obj1.notify();
System.out.println("threadA release the obj1 lock");
}catch (Exception e){
}
}
});
Thread threadB = new Thread(new Runnable() {
public void run() {
//先获取obj1
try {
// Thread.sleep(500);
synchronized (obj1) {
System.out.println("threadB get the obj1 lock");
synchronized (obj2) {
System.out.println("threadB get the obj2 lock");
// obj2.notify();
}
// obj2.notifyAll();
System.out.println("threadB release the obj2 lock");
}
System.out.println("threadB release the obj1 lock");
// obj1.notify();
} catch (Exception e) {
}
}
});
threadA.start();
threadB.start();
threadA.join();
threadB.join();
System.out.println("main thread is over...");
}
/*
这个例子证明:当线程被wait()阻塞后,只是剩下wait()语句出现之后的代码段还未执行。
当被唤醒时,线程会从wait()语句下一条语句开始执行。
也就是说阻塞的时候,保存了执行点情况
可也是为什么,notify/notifyAll需要搭配synchronized一起使用
*/
public static void test4() throws InterruptedException{
Thread threadA = new Thread(new Runnable() {
public void run() {
try{
synchronized (obj1){
System.out.println("此时获得obj1的公用监视器锁");
System.out.println("我还要等别人叫我...");
//再将自己挂到obj1 的阻塞队列上。
// 但是此时会自动释放公用监视器锁
obj1.wait();
System.out.println("我被唤醒了...");
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
public void run() {
try{
synchronized (obj2){
System.out.println("此时获得obj2的公用监视器锁");
// obj1.notifyAll(); //这样肯定报错。因为此线程获取的并不是obj1的监视器锁
}
System.out.println("threadB is over");
synchronized (obj1){
System.out.println("唤醒阻塞在obj1上的所有阻塞线程");
obj1.notifyAll();
}
}catch (Exception e){
e.printStackTrace();
}
}
});
threadA.start();
threadB.start();
threadA.join();
threadB.join();
System.out.println("over...");
}
}
② 线程sleep
package createThreadMethod;/*
name: demo02
user: ly
Date: 2020/5/29
Time: 15:26
*/
//该demo用于说明:当线程sleep的时候并不会放弃锁
//tips: yield方法与sleep方法类似。差别在于:
// yield方法调用后,当前线程让出CPU,即使当前线程还有时间片未用完。
// 此时系统再次调度。此时可能会再次调度到刚刚使用yield方法的那个线程。
// 而使用sleep时,当前线程也放弃CPU处于就绪态,但是直到sleep时间结束前都不会被调度到
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class demo02 {
static final Lock lock = new ReentrantLock();
static final Object obj1 = new Object();
public static void main(String []args){
test1();
test2();
}
//测试:sleep时是否会释放普通lock
public static void test1(){
Thread threadA = new Thread(new Runnable() {
public void run() {
//先拿到锁
System.out.println("threadA get the lock");
lock.lock();
try{
System.out.println("threadA is sleeping....");
Thread.sleep(10000);
System.out.println("threadA is awaking");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
System.out.println("threadA release the lock");
lock.unlock();
}
}
});
Thread threadB = new Thread(new Runnable() {
public void run() {
System.out.println("threadB get the lock");
//会等待10秒
lock.lock();
try{
System.out.println("threadB is sleeping....");
Thread.sleep(10000);
System.out.println("threadB is awaking");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
System.out.println("threadB release the lock");
lock.unlock();
}
}
});
threadA.start();
threadB.start();
System.out.println("haven't the function join, the main thread is over firstly");
}
//测试:sleep时是否会释放监视器锁
public static void test2(){
Thread threadA = new Thread(new Runnable() {
public void run() {
System.out.println("threadA getting the monitor lock");
try{
synchronized (obj1){
System.out.println("threadA is sleeping");
Thread.sleep(5000);
System.out.println("threadA is awaking");
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
public void run() {
System.out.println("threadB getting the monitor lock");
try{
synchronized (obj1){
System.out.println("threadBis sleeping");
// Thread.sleep(5000);
System.out.println("threadB is awaking");
}
}catch (Exception e){
e.printStackTrace();
}
}
});
threadA.start();
threadB.start();
System.out.println("haven't the function join, the main thread is over firstly");
}
}
③ 死锁
package createThreadMethod;/*
name: demo03
user: ly
Date: 2020/5/29
Time: 16:10
*/
import org.omg.CORBA.INITIALIZE;
// 该demo用于示例: 死锁。
public class demo03 {
private static Object object1 = new Object();
private static Object object2 = new Object();
public static void main(String []args){
Thread threadA = new Thread(new Runnable() {
public void run() {
try{
System.out.println("threadA is getting the object1...");
synchronized (object1){
System.out.println("threadA get the object1!");
System.out.println("threadA is getting the object2...");
Thread.sleep(2000); //睡眠,以便在当前线程获取object2之前,另一个线程就将其占据
//睡眠后获取object2
synchronized (object2){
System.out.println("threadA get the object2!");
}
}
}catch (InterruptedException e){
System.out.println("threadA is interrupted when it is sleeping or waitting...");
}
}
});
Thread threadB = new Thread(new Runnable() {
public void run() {
try{
System.out.println("threadB is getting the object2...");
synchronized (object2){
System.out.println("threadB get the object2!");
System.out.println("threadB is getting the object1...");
Thread.sleep(2000); //睡眠,
//睡眠后获取object1
synchronized (object1){
System.out.println("threadB get the object1!");
}
}
}catch (InterruptedException e){
System.out.println("threadB is interrupted when it is sleeping or waitting...");
}
}
});
threadA.start();
threadB.start();
System.out.println("the main thread is over when it is not use the function join");
}
}
④ ThreadLocal变量与 inheritanceThreadLcoal之间的区别
package createThreadMethod;/*
name: demo04
user: ly
Date: 2020/5/29
Time: 16:47
*/
// 本实例用于:展示普通的ThreadLocal变量与 inheritanceThreadLcoal之间的区别
public class demo04 {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
private static InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal();
public static void main(String []args){
// ordinaryThreadLocal();
inheritanceThreadLocal();
}
public static void ordinaryThreadLocal(){
threadLocal.set("the mainThread value");
Thread thread = new Thread(new Runnable() {
public void run() {
//尝试在子线程中获取父线程中的、不可继承ThreadLocal线程变量
System.out.println(Thread.currentThread()+":"+threadLocal.get());
}
});
thread.start();
//再尝试在主线程中获取。以做对比
System.out.println(Thread.currentThread()+":"+threadLocal.get());
}
public static void inheritanceThreadLocal(){
inheritableThreadLocal.set("the mainThread value");
Thread thread = new Thread(new Runnable() {
public void run() {
try{
Thread.sleep(2000);
//尝试在子线程中获取父线程中的、不可继承ThreadLocal线程变量
System.out.println(Thread.currentThread()+":"+inheritableThreadLocal.get());
}catch(InterruptedException e){
}
}
});
thread.start();
//再尝试在主线程中获取。以做对比
// inheritableThreadLocal.set("another main value"); //用这个语句可以看出:这个复制是值的复制,而非引用。
System.out.println(Thread.currentThread()+":"+inheritableThreadLocal.get());
}
}