当前位置: 代码迷 >> 综合 >> java多线程——java中进程与线程的操作、关系以及创建线程的三种方式
  详细解决方案

java多线程——java中进程与线程的操作、关系以及创建线程的三种方式

热度:22   发布时间:2024-02-08 11:47:44.0

类似于操作系统中的概念,每个线程都是共享其隶属的进程空间,如堆、方法区,
但是同时,每一个线程还是有一点属于自己的资源:程序计数器以及方法栈。

在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{@Overridepublic 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,这里定义为StringString 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{//先获取obj1synchronized (obj1){System.out.println("threadA get the resource1's lock...");//再尝试获取obj2synchronized (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先获取objectThread.sleep(1000);//再尝试去获取OBJ1synchronized (obj1){System.out.println("threadB get the resource1's lock...");
//                       拿到object1的锁后//然后再尝试拿到object2的锁synchronized (obj2){  //应该是拿不到的。因为threadA没有释放System.out.println("threadB get the resource2's lock...");//再释放object1System.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() {//先获取obj1try{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() {//先获取obj1try {
//                    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时是否会释放普通lockpublic 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之前,另一个线程就将其占据//睡眠后获取object2synchronized (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);  //睡眠,//睡眠后获取object1synchronized (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());}
}