进程与线程的区别:
进程
正在运行的程序,是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源
线程
进程中的单个顺序控制流,是一条执行路径
一个进程如果只有一条执行路径,则是单线程程序
一个进程内有多条执行路径,则是多线程程序
一个进程内可以执行多个任务,每个任务就是一个线程
多线程的意义
单进程计算机只能做一件事,现在计算机同一时间段内可以执行多个任务,提高CPU的利用率
多线程的意义
多线程的存在,不是提高程序的执行速度,其实是为了提高应用程序的使用率
程序执行是抢占CPU的资源,CPU的执行权
多个进程抢这些资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到
无法保证线程在什么时候抢到资源,线程执行有随机性
并发:逻辑上同时发生,指在某个时间内同时运行多个程序。
并行:物理上同时发生,指在某个时间点运行多个程序。、
Java程序运行原理
java命令启动java虚拟机,启动JVM,等于启动一个应用程序,也就是启动一个进程。
该进程会自动启动一个主线程,然后主线程去调用某个类的main方法,所以main方法运行在主线程中,
再次之前程序都是单线程的。
JVM是多线程,垃圾回收线程也要先东西,否则会很容易出现内存溢出。
最少启动了主线程和垃圾回收线程两个线程。
线程是依赖进程存在的,所以要先调用一个进程。进程由系统创建的,所以我们应该调用系统功能创建一个进程。
Java是不能调用系统功能的,所以我们没有办法直接实现多线程。Java可以调用C/C++写好的程序实现多线程,由C/C++调用系统功能创建进程,然后由Java去调用这样的东西,然后提供一些类供我们使用,就可以实现多线程程序。
实现多线程的方式
- 继承Thread
public class ThreadJava extends Thread {
//重写run()方法
public void run() {
}
}
- 实现Runnable接口
public class RunnableJava Implements Runnable{
//重写run()方法
public void run(){
}
}
执行多线程
如果是执行调用run()方法的话就会作为普通类执行,而不是多线程进行执行。
//继承Thread类的执行方式
ThreadJava tj = new ThreadJava();
tj.start()
//实现Runnable接口的执行方式
RunnableJava rj = new RunnableJava();
Thread t = new Thread(rj);
t.start();
Thread
/*
* 两个线程分别得到i的值,分别执行run()方法体
* */
public class newTest extends Thread{
private int i = 100;
public void run() {
while (true) {
if (i > 0) {
System.out.println("i的值为: " + (i--) + "--------" + Thread.currentThread().getName());
}
}
}
public static void main(String[] srgs) {
newTest nt = new newTest();
newTest nt2 = new newTest();
nt.start();
nt2.start();
}
}
/*
* 两个线程共用i
* */
public class newTest extends Thread{
private static int i = 100;
public void run() {
while (true) {
if (i > 0) {
System.out.println("i的值为: " + (i--) + "--------" + Thread.currentThread().getName());
}
}
}
public static void main(String[] srgs) {
newTest nt = new newTest();
newTest nt2 = new newTest();
nt.start();
nt2.start();
}
}
这里主要看i是类变量还是成员变量,类变量任意类对象都可以对它进行更改,成员变量因为对象的不同而不同。
实现Runnable接口
程序一:下面程序每次输出i的值减一
public class RunnableJava implements Runnable{
private int i = 100;
private Object obj = new Object();
public void run() {
while (true) {
synchronized (obj) {
obj.notifyAll();
if (i > 0) {
System.out.println("i的值为: " + (i--) + "--------" + Thread.currentThread().getName());
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
obj.wait(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
//---------------------------------------------------------------------------------------------------------------------------------
RunnableJava rj1 = new RunnableJava();
Thread t1 = new Thread(rj1, "线程rj1 ---------- 线程一");
Thread t2 = new Thread(rj1, "线程rj1 ---------- 线程二");
Thread t3 = new Thread(rj1, "线程rj1 ---------- 线程三");
t1.start();
t2.start();
t3.start();
程序二:如果建两个Runnable的实现类对象分别传入多个Thread中,如果想让两个Runnable分别计算输出i的值,i不能用static修饰,锁对象可用static修饰,也可以不用。不过这里是有区别的,不用static代表分别传入到Thread的Runnable的两个内存每次只能有一个Thread访问,使用static的话同一只能有一个线程访问两个Runnable内存中的一块,也就是只能执行一个Runnable的run()方法
public class RunnableJava implements Runnable{
private int i = 100;
private static Object obj = new Object();
public void run() {
while (true) {
synchronized (obj) {
obj.notifyAll();
if (i > 0) {
System.out.println("i的值为: " + (i--) + "--------" + Thread.currentThread().getName());
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
obj.wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class RunnableJava implements Runnable{
private int i = 100;
private Object obj = new Object();
public void run() {
while (true) {
synchronized (obj) {
obj.notifyAll();
if (i > 0) {
System.out.println("i的值为: " + (i--) + "--------" + Thread.currentThread().getName());
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
obj.wait(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
RunnableJava rj1 = new RunnableJava();
RunnableJava rj2 = new RunnableJava();
Thread t1 = new Thread(rj1, "线程rj1 ---------- 线程一");
Thread t2 = new Thread(rj1, "线程rj1 ---------- 线程二");
Thread t3 = new Thread(rj1, "线程rj1 ---------- 线程三");
t1.start();
t2.start();
t3.start();
Thread t4 = new Thread(rj2, "线程rj2 ---------- 线程四");
Thread t5 = new Thread(rj2, "线程rj2 ---------- 线程五");
Thread t6 = new Thread(rj2, "线程rj2 ---------- 线程六");
t4.start();
t5.start();
t6.start();
程序三:如果建两个Runnable的实现类对象分别传入多个Thread中,如果想让两个Runnable共同计算输出j的值,i不和锁对象必须用static修饰
public class RunnableJava implements Runnable{
private static int i = 100;
private static Object obj = new Object();
public void run() {
while (true) {
synchronized (obj) {
obj.notifyAll();
if (i > 0) {
System.out.println("i的值为: " + (i--) + "--------" + Thread.currentThread().getName());
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
obj.wait(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
这里要是静态变量的知识,static修饰的变量是属于类的,不管你new几个对象,都只会有这一个对象。所以
程序一,只有一个Runnable对象传入Thread,访问的是这个Runnable对象的内存,这里只有一个i和锁对象,所以不需要使用static修饰;
程序二,有两个Runnable对象分别传入Thread,这里有两块内存,要分别计算输出i 的值,i就必须每个对象给一个,但是锁对象可以是唯一的,也可以不是唯一的。锁对象static修饰代表同一时间只能有一个run()方法执行操作,不用static修饰代表同一时间段传入两个Runnable对象的两个线程可以同时被执行;
程序三,i的值和锁对象都使用static修饰,代表不管有几个Runnable对象传入多少个Thread中,变量i从100递减到1只会执行一次,和程序一结果相同。
上面就是看内存是否访问的是同一个变量,使用的锁对象是否是同一个对象,锁住之后的方法块每次都只能一个得到锁对象的线程可以执行。
线程同步synchronized
通过synchronized关键字实现线程同步,synchronized是不被继承的
1、synchronized同步代码块,同步代码块传入的锁对象可以是任意对象
//线程必须得到锁才能执行同步代码块,否则无法执行同步代码块
public class SynchronizedTest2 implements Runnable {
private static int i = 0;
private static int j = 100;
//锁对象不能为空值,定义为类变量则为所有类对象共用
private static Object obj1 = new Object();
private static Object obj2 = new Object();
@Override
public void run() {
while(true) {
synchronized (obj1) {
if(i < 100) {
System.out.println(Thread.currentThread().getName() + "---------" + (i++));
}
}
synchronized (obj2) {
if(j > 0) {
System.out.println(Thread.currentThread().getName() + "---------" + (j--));
}
}
}
}
}
2、synchronized修饰成员方法,修饰成员方法是锁对象问对象本身,也就是this
/*
* synchronized修饰普通方法对象锁是本身,即this
* */
public class SynchronizedMethod2 implements Runnable{
public synchronized void method() {
System.out.println("synchronized修饰普通方法" + Thread.currentThread().getName());
}
public void run() {
// TODO Auto-generated method stub
while(true) {
this.method();
synchronized(this) {
System.out.println("synchronized修饰同步代码块" + Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
SynchronizedMethod2 sm = new SynchronizedMethod2();
Thread t1 = new Thread(sm, "线程一");
SynchronizedMethod2 sm2 = new SynchronizedMethod2();
//传入sm2的时候锁是this sm2
//传入sm的时候锁是this sm
//Thread t2 = new Thread(sm2, "线程二");
Thread t2 = new Thread(sm, "线程二");
t1.start();
t2.start();
}
}
3、synchronized修饰静态方法,修饰静态方法的锁对象是类本身的.class文件
/* synchronized(类.class)锁住的是整个类
* synchronized修饰静态方法和同步代码块,在两个方法体中都sleep()使得其他线程有机会得到执行
* 不管创建了多少个Runnable对象传入到不同的Thread中,每次只能有一个Thread执行synchronized(类.class)代码块,同synchronized修饰的静态方法
* */
public class SynchronizedMethod implements Runnable {
private static int i = 20;
private int j = 0;
private static Object obj = new Object();
public synchronized static void method1() {
//SynchronizedMethod.class.notifyAll();
System.out.println("synchronized修饰静态方法:" + "---" + Thread.currentThread().getName());
if (i > 0) {
System.out.println("静态方法中i的值: " + (i--) + "---" + Thread.currentThread().getName());
System.out.println("静态方法执行完");
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// try {
// SynchronizedMethod.class.wait();
// } catch (InterruptedException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }
}
}
public void run() {
// 方法的调用放在while()循环外只会执行method1()方法
// method1();
while (true) {
method1();
//同步代码块锁对象为类的class文件,则是锁住整个类的,其他线程必须等代码块执行完释放锁才能继续执行
synchronized (SynchronizedMethod.class) {
//把同步代码块锁对象换成obj,线程之间互无关系,可以任意执行自己对象的锁
//synchronized (obj) {
//SynchronizedMethod.class.notifyAll();
System.out.println("synchronized使用类class文件修饰同步代码块" + "---" + Thread.currentThread().getName());
if (j < 20) {
System.out.println("同步代码块中j的值: " + (j++) + "---" + Thread.currentThread().getName());
System.out.println("同步代码块执行完");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// try {
// SynchronizedMethod.class.wait();
// } catch (InterruptedException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }
}
}
}
}
}
4、synchronized修饰run()方法,代表某个线程只能有一个run()方法执行
public class SynchronizedRun implements Runnable{
public static void main(String[] args) {
// TODO Auto-generated method stub
SynchronizedRun sr = new SynchronizedRun();
Thread t1 = new Thread(sr, "线程一");
//SynchronizedRun sr2 = new SynchronizedRun();
Thread t2 = new Thread(sr, "线程二");
t1.start();
t2.start();
}
//等于synchronized(this),可以保证一个线程只能有一个run()方法在运行
//synchronized(this)在没有释放锁的情况下只有一个线程能够执行这个方法
public synchronized void run() {
// TODO Auto-generated method stub
System.out.println("多线程run方法" + Thread.currentThread().getName());
}
}
synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。就上面程序二的类,创建两个Runnable对象,分别传入到Thread中,每一个Runnable对象的同步代码块有一个锁对象obj,两个Runnable对象传到的Thread多线程可以同时执行同步代码块中的代码。而同一个Runnable对象下的多线程之间互斥,谁得到锁谁执行。所以synchronized锁住的是对象不是代码块。
static synchronized方法,static方法可以直接类名加方法名调用,方法中无法使用this,所以它锁的不是this,而是类的Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了整个代码段。同步代码块如果想要全局锁可以传入类本身的class文件。
线程同步Lock
1、lock方法,没有获取锁一直等待
public class LockJava implements Runnable {
private static int i = 100;
private static Lock lock = new ReentrantLock();
// private static Condition c = lock.newCondition();
public void run() {
while(true) {
lock.lock();
try {
// c.signalAll();
if(i > 0) {
System.out.println(Thread.currentThread().getName() + "----" + (i--));
// try {
// c.await();
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
}
}finally {
lock.unlock();
}
}
}
}
2、tryLock()方法
tryLock()方法返回一个boolean值,判断是否可以获取锁,并立即返回结果。所以tryLock()和lock()方法一样使用的话如果没有获取锁只会返回false,但是在unLock()的时候会报错,因为并没有获取锁。不过报错的情况并不是使程序终止
/*
* IllegalMonitorStateException异常
* 线程一运行获得lock1,等待,
* 线程二运行获得lock2,等待,
* 线程一执行lock2锁锁住的部分,并不能获取锁,
* */
public class DeathLock1 {
static Lock lock1 = new ReentrantLock();
static Lock lock2 = new ReentrantLock();
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName() + "开始运行:");
lock1.lock();
try {
System.out.println(Thread.currentThread().getName() + "get lock1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//lock2.lock();
//这里并没有获得锁
lock2.tryLock();
try {
System.out.println(Thread.currentThread().getName() + "get lock2");
}finally {
//这里会报IllegalMonitorStateException错误
//因为此时锁不在这里
lock2.unlock();
}
}finally {
lock1.unlock();
}
}
}
}, "线程一").start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
System.out.println(Thread.currentThread().getName() + "开始运行:");
lock2.lock();
try {
System.out.println(Thread.currentThread().getName() + "get lock2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//lock1.lock();
lock1.tryLock();
try {
//boolean booleanlock1 = lock1.tryLock();
System.out.println(Thread.currentThread().getName() + "get lock1");
}finally {
//没有获取锁,会报错
lock1.unlock();
}
}finally {
System.out.println("线程二释放lock2");
lock2.unlock();
}
}
}
}, "线程二").start();
}
}
可以使用if语句,tryLock()作为条件,获取了就执行,不获取就不执行
public class DeathLock1 {
static Lock lock1 = new ReentrantLock();
static Lock lock2 = new ReentrantLock();
static Condition c1 = lock1.newCondition();
static Condition c2 = lock2.newCondition();
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + "开始运行:");
lock1.lock();
try {
System.out.println(Thread.currentThread().getName() + "get lock1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程二释放了lock2,这里就可以获取
if (lock2.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + "get lock2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock2.unlock();
}
}
} finally {
System.out.println("线程一释放了lock1");
lock1.unlock();
}
}
}
}, "线程一").start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + "开始运行:");
lock2.lock();
try {
System.out.println(Thread.currentThread().getName() + "get lock2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
System.out.println("线程二释放了lock2");
lock2.unlock();
}
}
}, "线程二").start();
}
}
tryLock()不是lock()那种使用方式
lock.tryLock() //这里返回的是boolean,这里不会报错
...
lock.unlock() //这里没有获取锁的话会报错
3.lockInterruptibly,没有当前线程没有被中断,则获取锁
允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。而ReentrantLock.lock方法不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态。
线程同步读写锁
public class LockWriterReaderJava {
private static int i = 0;
private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private static ReentrantReadWriteLock.ReadLock read = lock.readLock();
private static ReentrantReadWriteLock.WriteLock write = lock.writeLock();
public static void main(String[] args) {
// TODO Auto-generated method stub
//读,可以随意访问
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
write.lock();
try {
if(i < 100) {
System.out.println(Thread.currentThread().getName() + (i++) + "----i增加1");
}
}finally {
write.unlock();
}
read.lock();
try {
if(i < 100) {
System.out.println(Thread.currentThread().getName() + i);
}
}finally {
read.unlock();
}
}
}
}, "线程一").start();
//写,每次只能一个线程进行访问
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
read.lock();
try {
if(i < 100) {
System.out.println(Thread.currentThread().getName() + i);
}
}finally {
read.unlock();
}
}
}
}, "线程二").start();
}
}
写锁可以“降级”为读锁;读锁不能“升级”为写锁。在线程持有读锁的情况下,该线程不能取得写锁(因为获取写锁的时候,如果发现当前的读锁被占用,就马上获取失败,不管读锁是不是被当前线程持有)在线程持有写锁的情况下,该线程可以继续获取读锁(获取读锁时如果发现写锁被占用,只有写锁没有被当前线程占用的情况才会获取失败)。
死锁
/*
* 线程一开始运行: 线程一get到obj1 线程二开始运行: 线程二get到obj2
*
* 线程一运行获得obj1,到sleep()方法,这个时候 线程二运行获得obj2,到sleep()方法
* 此时线程一等待线程二释放obj2锁继续运行,线程二等到obj1继续运行,都没等到,所以死锁。
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
System.out.println(Thread.currentThread().getName() + "开始运行:");
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "get到obj1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + "get到obj2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}, "线程一").start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
System.out.println(Thread.currentThread().getName() + "开始运行:");
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + "get到obj2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "get到obj1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}, "线程二").start();
}
}
线程调度
线程状态: